react-native,redux,redux-saga组合开发实现

react

简单的说搞react开发的痛点之一,单向数据流的传递,redux统一管理数据,redux-saga又处理管理了异步调用。

要实现的内容如下,界面


目录结构


首先从请求接口入手,用axios封装请求接口,统一处理请求 axios.js

import axios from 'axios'

let defaultConfig = {

timeout: 3000,

}

let instance = axios

class Axios {

constructor(props) {

if (props && typeof props == 'object') {

instance = axios.create(props)

} else {

instance = axios.create(defaultConfig);

}

//拦截

instance.interceptors.request.use((config) => {

return config;

}, (error) => {

console.log(error)

return Promise.reject(error);

});

//日志 响应结果

instance.interceptors.response.use((response) => {

return response.data;

}, (error) => {

console.log(error)

return Promise.reject(error);

})

}

send(params) {

if (!params || typeof params != 'object') {

throw new Error('params is undefined or not an object')

}

if (params.method == 'GET') {

return get(params.url)

} else if (params.method == 'POST') {

return post(params.url, params)

}

}

}

async function get(url, callback) {

try {

let response = await instance.get(url)

return response

} catch (e) {

console.log(e)

}

}

async function post(url, params, callback) {

try {

let response = await instance.post(url)

//eturn callback(response)

return response

} catch (e) {

console.log(e)

}

}

export default Instance = new Axios();

store.js 管理以及开发环境下的及时更新

const sagamiddleware = createSagaMiddleware();

export default function configureStore(initStore = {}) {

const middlewares = [sagamiddleware];

if (__DEV__) {

middlewares.push(logger)

}

const createStoreMiddleware = applyMiddleware(...middlewares)(createStore);

const store = createStoreMiddleware(

createReducer(), initStore

);

store.runSaga = sagamiddleware.run;

store.close = () => store.dispatch(END);

// Make reducers hot reloadable, see http://mxs.is/googmo

/* istanbul ignore next */

if (module.hot) {

module.hot.accept(() => {

const nextRootReducer = require('../reducers/index').default; //reducers 文件下的 index

store.replaceReducer(createReducer(nextRootReducer))

}, )

}

return store

}


reducers 文件下的 index.js, combineReducers各个模块的 reducer

import { combineReducers } from 'redux';

import { latestNews } from './latestNewsReducer';

import { special } from "./specialReducer";

import { themes } from "./themesReducer";

export default createReducer = (injectedReducers) => {

return combineReducers({

latestNews: latestNews,

special: special,

themes: themes,

router: router,

...injectedReducers

})

}


接下来就是 各个模块的 reducer了,接受action 返回的 state 或者data,由于都是get请求,各个模块的请求都大同小异,以最新模块为例,   latestNewsReducer.js 如下

import { RQUEST_LATESTNEWS, SUC_LATESTNEWS, DESTORY_LATESTNEWS } from '../actions/latestnews/types';

export const latestNews = (state = null, action) => {

switch (action.type) {

case RQUEST_LATESTNEWS:

return state

case SUC_LATESTNEWS:

return Object.assign({}, state, {

data: action.data

})

case DESTORY_LATESTNEWS:

return null

default:

return state;

}

}


type 为常理单独写出来的 理应 单独新建 const 目录用于放各个模块的 type,图快就都挨着action放了 

还是 以 最新模块 为例子 type.js

//进入请求请求 

export const FETCHED_LATESTNEWS = 'fetched_latestnews'

//发送请求

export const RQUEST_LATESTNEWS = 'request_latestnews'

//请求成功 返回数据

export const SUC_LATESTNEWS = 'suc_latestnews'

//销毁

export const DESTORY_LATESTNEWS = 'destory_latestnews' //当离开当前页面时 返回此 置空stroe对应的值

latestNews的action 也很简单

import { RQUEST_LATESTNEWS, SUC_LATESTNEWS, FETCHED_LATESTNEWS, DESTORY_LATESTNEWS } from './types';

//初始请求

export const fetchedLatestNews = () => {

return {

type: FETCHED_LATESTNEWS

}

}

//开始发送请求

export const requestLatestNews = () => {

return {

type: RQUEST_LATESTNEWS

}

}

//请求成功

export const sucLatestNews = (data) => {

return {

type: SUC_LATESTNEWS,

data

}

}

//销毁

export const destoryLatestnews = () => {

return {

type: DESTORY_LATESTNEWS

}

}

现在开始sagas的编写

-------------------------------------------------嗯··---------------------------------------------


-------------------------------------------------辣眼睛---------------------------------------------

sagas目录下index 统一引入各个模块 对应的 请求方法

index.js

import { all, takeEvery, fork } from 'redux-saga/effects';

import { FETCHED_LATESTNEWS } from '../actions/latestnews/types'

import { getLatestNews } from './latestnews';

import { FETCHED_SPECICAL } from '../actions/special/types';

import { getSpecial } from './special';

import { FETCHED_THEMES } from '../actions/themes/types';

import { getThemes } from './themes';

export default function* rootSaga() {

yield takeEvery(FETCHED_LATESTNEWS, getLatestNews);

yield takeEvery(FETCHED_THEMES, getThemes);

yield takeEvery(FETCHED_SPECICAL, getSpecial);

}


还是以最新为例:

import { put, call } from 'redux-saga/effects';

import { SUC_LATESTNEWS } from '../actions/latestnews/types';

import { repoLoadingError, requestLatestNews, sucLatestNews } from "../actions/latestnews/index";

import { GetLatestNews } from '../apis/latestnews/fetch';

export function* getLatestNews() {

try {

yield put(requestLatestNews())

const data = yield call(GetLatestNews);

yield put({type: SUC_LATESTNEWS, data});

} catch (error) {

yield put(repoLoadingError(error));

}

}

现在到容器了

container, 对应的 最新模块的container

latestnews.js  是嵌套在第个 tabNavigator 里的 ,和热门平级, tabNavigator 配置都大同小异 此处省略···

import React, { Component } from 'react';

import { View, Text } from 'react-native';

import { bindActionCreators } from 'redux';

import { connect } from 'react-redux';

import { requestLatestNews, sucLatestNews, fetchedLatestNews } from '../../actions/latestnews/index';

class LatestNews extends Component {

componentDidMount() {

this.props.fetchedLatestNews()

}

render() {

let list = this.props.latestNews;

return (

<View>

{list ? <Text>{JSON.stringify(this.props.latestNews)}

</Text> : <Text>LOADING</Text>

}

</View>

)

}

}

const mapStateToProps = (state) => {

return {

latestNews: state.latestNews

}

}

const matchDispatchToProps = (dispatch) => {

return bindActionCreators({

fetchedLatestNews: fetchedLatestNews,

}, dispatch)

}

export default connect(mapStateToProps, matchDispatchToProps)(LatestNews);

进行到这里,配置react-navigation, 



tabNavigator.js

import React, { Component } from 'react';

import { View, Image, StyleSheet } from "react-native";

import { TabNavigator } from "react-navigation";

import News from '../containers/latestnews';

import Themes from '../containers/themes';

import Special from '../containers/special';

export default tabNavigator = TabNavigator(

{

newsTab: {

screen: News,

navigationOptions: {

tabBarLabel: '最新',

tabBarIcon: ({ tintColor, focused }) => (

<Image resizeMode='contain'

source={require('../icon/icon_latestnews.png')}

style={[style.footImage, {tintColor: tintColor}]}

/>

)

}

},

Themes: {

screen: Themes,

navigationOptions: {

tabBarLabel: '主题',

tabBarIcon: ({ tintColor, focused }) => (

<Image resizeMode='contain'

source={require('../icon/icon_themes.png')}

style={[style.footImage, {tintColor: tintColor}]}

/>

)

}

},

Special: {

screen: Special,

navigationOptions: {

tabBarLabel: '专栏',

tabBarIcon: ({ tintColor, focused }) => (

<Image resizeMode='contain'

source={require('../icon/icon_special.png')}

style={[style.footImage, {tintColor: tintColor}]}

/>

)

}

},

}, {

backBehavior: 'none',

tabBarPosition: 'bottom',

lazy: true,

lazyLoad: true,

initialRouteName: 'newsTab',

tabBarOptions: {

showIcon: true,

pressOpacity: 0.8,

style: {

height: 45,

backgroundColor: '#ffffff',

zIndex: 0,

position: 'relative'

},

labelStyle: {

fontSize: 12,

paddingVertical: 0,

marginTop: 0

},

iconStyle: {

marginTop: -5

},

tabStyle: {

backgroundColor: '#eeeeee',

},

inactiveTintColor: '#212121',

activeTintColor: '#0084ff'

}

}

)

let style = StyleSheet.create({

footImage: {

width: 24,

height: 24

},

});

三个大模块,最新,主题和专栏都放在 一个tabNavigator里,再配置到StackNavigator,最新里还嵌套一个 tabNavigator, 也可以把三个主题都放 stackNavigator里。放在stackNavigator,某些方面会更简单也更直观


import React, { Component } from 'react';

import { bindActionCreators } from 'redux';

import { connect } from 'react-redux';

import { addNavigationHelpers, StackNavigator } from 'react-navigation';

import tabNavigator from './tabNavigator';

export const Navigator = StackNavigator({

tab: { screen: tabNavigator }

},{

navigationOptions: {

header: null,

headerBackTitle: null,

headerTintColor: '#333333',

showIcon: false,

swipeEnabled: false,

animationEnabled: false,

initialRouteName: 'tab',

lazy: true,

},

mode: 'card',

lazy: true,

});

export default Navigator;

当页面滑动离开当前模块 进入下一个模块 ,减小开支,可以选择把离开模块的store 清空,修改入口文件如下

调用onNavigationStateChange 方法

class Index extends Component {

_onNavigationStateChange = (prevState, nextState) => {

let prev = prevState.routes[0],

preIndex = prev.index,

preRouteName = prev.routes[preIndex].key,

switch (preRouteName) {

case 'newsTab':

store.dispatch(destoryLatestnews())

break;

case 'Themes':

store.dispatch(destoryThemes())

break;

default:

store.dispatch(destorySpecial())

break;

}

}

render() {

return (

<Provider store={store}>

<AppStackNavigator onNavigationStateChange={(prevState, currentState) => {

this._onNavigationStateChange(prevState, currentState)

}}/>

</Provider>

);

}

}

export default Index;


本来想在页面离开,组件销毁时 在componentWillUnmount 里调用 destoryLatesNews() 这些方法的,但是滑动切换的时候组件并不会销毁,那么只有更新周期函入手了。

---------------------------------------------------------------------

ps: 知道怎么在componentWillUnmount 里调用 destoryLatesNews 方法或者 进入到此函数的敬请指点,求告知呀

---------------------------------------------------------------------

那么就需要修改一下 latestnews 进入到在更新周期函数中如下写入

shouldComponentUpdate(nextProps, nextState) {

if (nextProps.navigation.state.routeName === 'newsTab' && this.props.latestNews == null) {

this.props.fetchedLatestNews()

return true;

}

return false;

}





大体流程就这样了,当然redux-saga 各个函数方法的用途还需多理解,为什么 没进入 componentWillUnmount

是为什么呢·····哈哈哈哈· 等告知·····

有需要的交流的可以加个好友


简单的说搞react开发的痛点之一,单向数据流的传递,redux统一管理数据,redux-saga又处理管理了异步调用。

要实现的内容如下,界面


目录结构


首先从请求接口入手,用axios封装请求接口,统一处理请求 axios.js

import axios from 'axios'

let defaultConfig = {

timeout: 3000,

}

let instance = axios

class Axios {

constructor(props) {

if (props && typeof props == 'object') {

instance = axios.create(props)

} else {

instance = axios.create(defaultConfig);

}

//拦截

instance.interceptors.request.use((config) => {

return config;

}, (error) => {

console.log(error)

return Promise.reject(error);

});

//日志 响应结果

instance.interceptors.response.use((response) => {

return response.data;

}, (error) => {

console.log(error)

return Promise.reject(error);

})

}

send(params) {

if (!params || typeof params != 'object') {

throw new Error('params is undefined or not an object')

}

if (params.method == 'GET') {

return get(params.url)

} else if (params.method == 'POST') {

return post(params.url, params)

}

}

}

async function get(url, callback) {

try {

let response = await instance.get(url)

return response

} catch (e) {

console.log(e)

}

}

async function post(url, params, callback) {

try {

let response = await instance.post(url)

//eturn callback(response)

return response

} catch (e) {

console.log(e)

}

}

export default Instance = new Axios();

store.js 管理以及开发环境下的及时更新

const sagamiddleware = createSagaMiddleware();

export default function configureStore(initStore = {}) {

const middlewares = [sagamiddleware];

if (__DEV__) {

middlewares.push(logger)

}

const createStoreMiddleware = applyMiddleware(...middlewares)(createStore);

const store = createStoreMiddleware(

createReducer(), initStore

);

store.runSaga = sagamiddleware.run;

store.close = () => store.dispatch(END);

// Make reducers hot reloadable, see http://mxs.is/googmo

/* istanbul ignore next */

if (module.hot) {

module.hot.accept(() => {

const nextRootReducer = require('../reducers/index').default; //reducers 文件下的 index

store.replaceReducer(createReducer(nextRootReducer))

}, )

}

return store

}


reducers 文件下的 index.js, combineReducers各个模块的 reducer

import { combineReducers } from 'redux';

import { latestNews } from './latestNewsReducer';

import { special } from "./specialReducer";

import { themes } from "./themesReducer";

export default createReducer = (injectedReducers) => {

return combineReducers({

latestNews: latestNews,

special: special,

themes: themes,

router: router,

...injectedReducers

})

}


接下来就是 各个模块的 reducer了,接受action 返回的 state 或者data,由于都是get请求,各个模块的请求都大同小异,以最新模块为例,   latestNewsReducer.js 如下

import { RQUEST_LATESTNEWS, SUC_LATESTNEWS, DESTORY_LATESTNEWS } from '../actions/latestnews/types';

export const latestNews = (state = null, action) => {

switch (action.type) {

case RQUEST_LATESTNEWS:

return state

case SUC_LATESTNEWS:

return Object.assign({}, state, {

data: action.data

})

case DESTORY_LATESTNEWS:

return null

default:

return state;

}

}


type 为常理单独写出来的 理应 单独新建 const 目录用于放各个模块的 type,图快就都挨着action放了 

还是 以 最新模块 为例子 type.js

//进入请求请求 

export const FETCHED_LATESTNEWS = 'fetched_latestnews'

//发送请求

export const RQUEST_LATESTNEWS = 'request_latestnews'

//请求成功 返回数据

export const SUC_LATESTNEWS = 'suc_latestnews'

//销毁

export const DESTORY_LATESTNEWS = 'destory_latestnews' //当离开当前页面时 返回此 置空stroe对应的值

latestNews的action 也很简单

import { RQUEST_LATESTNEWS, SUC_LATESTNEWS, FETCHED_LATESTNEWS, DESTORY_LATESTNEWS } from './types';

//初始请求

export const fetchedLatestNews = () => {

return {

type: FETCHED_LATESTNEWS

}

}

//开始发送请求

export const requestLatestNews = () => {

return {

type: RQUEST_LATESTNEWS

}

}

//请求成功

export const sucLatestNews = (data) => {

return {

type: SUC_LATESTNEWS,

data

}

}

//销毁

export const destoryLatestnews = () => {

return {

type: DESTORY_LATESTNEWS

}

}

现在开始sagas的编写

-------------------------------------------------嗯··---------------------------------------------


-------------------------------------------------辣眼睛---------------------------------------------

sagas目录下index 统一引入各个模块 对应的 请求方法

index.js

import { all, takeEvery, fork } from 'redux-saga/effects';

import { FETCHED_LATESTNEWS } from '../actions/latestnews/types'

import { getLatestNews } from './latestnews';

import { FETCHED_SPECICAL } from '../actions/special/types';

import { getSpecial } from './special';

import { FETCHED_THEMES } from '../actions/themes/types';

import { getThemes } from './themes';

export default function* rootSaga() {

yield takeEvery(FETCHED_LATESTNEWS, getLatestNews);

yield takeEvery(FETCHED_THEMES, getThemes);

yield takeEvery(FETCHED_SPECICAL, getSpecial);

}


还是以最新为例:

import { put, call } from 'redux-saga/effects';

import { SUC_LATESTNEWS } from '../actions/latestnews/types';

import { repoLoadingError, requestLatestNews, sucLatestNews } from "../actions/latestnews/index";

import { GetLatestNews } from '../apis/latestnews/fetch';

export function* getLatestNews() {

try {

yield put(requestLatestNews())

const data = yield call(GetLatestNews);

yield put({type: SUC_LATESTNEWS, data});

} catch (error) {

yield put(repoLoadingError(error));

}

}

现在到容器了

container, 对应的 最新模块的container

latestnews.js  是嵌套在第个 tabNavigator 里的 ,和热门平级, tabNavigator 配置都大同小异 此处省略···

import React, { Component } from 'react';

import { View, Text } from 'react-native';

import { bindActionCreators } from 'redux';

import { connect } from 'react-redux';

import { requestLatestNews, sucLatestNews, fetchedLatestNews } from '../../actions/latestnews/index';

class LatestNews extends Component {

componentDidMount() {

this.props.fetchedLatestNews()

}

render() {

let list = this.props.latestNews;

return (

<View>

{list ? <Text>{JSON.stringify(this.props.latestNews)}

</Text> : <Text>LOADING</Text>

}

</View>

)

}

}

const mapStateToProps = (state) => {

return {

latestNews: state.latestNews

}

}

const matchDispatchToProps = (dispatch) => {

return bindActionCreators({

fetchedLatestNews: fetchedLatestNews,

}, dispatch)

}

export default connect(mapStateToProps, matchDispatchToProps)(LatestNews);

进行到这里,配置react-navigation, 



tabNavigator.js

import React, { Component } from 'react';

import { View, Image, StyleSheet } from "react-native";

import { TabNavigator } from "react-navigation";

import News from '../containers/latestnews';

import Themes from '../containers/themes';

import Special from '../containers/special';

export default tabNavigator = TabNavigator(

{

newsTab: {

screen: News,

navigationOptions: {

tabBarLabel: '最新',

tabBarIcon: ({ tintColor, focused }) => (

<Image resizeMode='contain'

source={require('../icon/icon_latestnews.png')}

style={[style.footImage, {tintColor: tintColor}]}

/>

)

}

},

Themes: {

screen: Themes,

navigationOptions: {

tabBarLabel: '主题',

tabBarIcon: ({ tintColor, focused }) => (

<Image resizeMode='contain'

source={require('../icon/icon_themes.png')}

style={[style.footImage, {tintColor: tintColor}]}

/>

)

}

},

Special: {

screen: Special,

navigationOptions: {

tabBarLabel: '专栏',

tabBarIcon: ({ tintColor, focused }) => (

<Image resizeMode='contain'

source={require('../icon/icon_special.png')}

style={[style.footImage, {tintColor: tintColor}]}

/>

)

}

},

}, {

backBehavior: 'none',

tabBarPosition: 'bottom',

lazy: true,

lazyLoad: true,

initialRouteName: 'newsTab',

tabBarOptions: {

showIcon: true,

pressOpacity: 0.8,

style: {

height: 45,

backgroundColor: '#ffffff',

zIndex: 0,

position: 'relative'

},

labelStyle: {

fontSize: 12,

paddingVertical: 0,

marginTop: 0

},

iconStyle: {

marginTop: -5

},

tabStyle: {

backgroundColor: '#eeeeee',

},

inactiveTintColor: '#212121',

activeTintColor: '#0084ff'

}

}

)

let style = StyleSheet.create({

footImage: {

width: 24,

height: 24

},

});

三个大模块,最新,主题和专栏都放在 一个tabNavigator里,再配置到StackNavigator,最新里还嵌套一个 tabNavigator, 也可以把三个主题都放 stackNavigator里。放在stackNavigator,某些方面会更简单也更直观


import React, { Component } from 'react';

import { bindActionCreators } from 'redux';

import { connect } from 'react-redux';

import { addNavigationHelpers, StackNavigator } from 'react-navigation';

import tabNavigator from './tabNavigator';

export const Navigator = StackNavigator({

tab: { screen: tabNavigator }

},{

navigationOptions: {

header: null,

headerBackTitle: null,

headerTintColor: '#333333',

showIcon: false,

swipeEnabled: false,

animationEnabled: false,

initialRouteName: 'tab',

lazy: true,

},

mode: 'card',

lazy: true,

});

export default Navigator;

当页面滑动离开当前模块 进入下一个模块 ,减小开支,可以选择把离开模块的store 清空,修改入口文件如下

调用onNavigationStateChange 方法

class Index extends Component {

_onNavigationStateChange = (prevState, nextState) => {

let prev = prevState.routes[0],

preIndex = prev.index,

preRouteName = prev.routes[preIndex].key,

switch (preRouteName) {

case 'newsTab':

store.dispatch(destoryLatestnews())

break;

case 'Themes':

store.dispatch(destoryThemes())

break;

default:

store.dispatch(destorySpecial())

break;

}

}

render() {

return (

<Provider store={store}>

<AppStackNavigator onNavigationStateChange={(prevState, currentState) => {

this._onNavigationStateChange(prevState, currentState)

}}/>

</Provider>

);

}

}

export default Index;


本来想在页面离开,组件销毁时 在componentWillUnmount 里调用 destoryLatesNews() 这些方法的,但是滑动切换的时候组件并不会销毁,那么只有更新周期函入手了。

---------------------------------------------------------------------

ps: 知道怎么在componentWillUnmount 里调用 destoryLatesNews 方法或者 进入到此函数的敬请指点,求告知呀

---------------------------------------------------------------------

那么就需要修改一下 latestnews 进入到在更新周期函数中如下写入

shouldComponentUpdate(nextProps, nextState) {

if (nextProps.navigation.state.routeName === 'newsTab' && this.props.latestNews == null) {

this.props.fetchedLatestNews()

return true;

}

return false;

}





大体流程就这样了,当然redux-saga 各个函数方法的用途还需多理解,为什么 没进入 componentWillUnmount

是为什么呢·····哈哈哈哈· 等告知·····

有需要的交流的可以加个好友


以上是 react-native,redux,redux-saga组合开发实现 的全部内容, 来源链接: utcz.com/z/383582.html

回到顶部