前面的文章中,我们介绍过,React在发送Ajax等异步请求的时候,我们应该将异步请求的逻辑放在componentDidMount
钩子函数中。但是有一个问题,当一个组件中有很多个异步请求操作,都放在这个钩子函数中,那么这个钩子函数会显得很臃肿,该怎么解决这个问题了? 在React中我们可以通过引入中间件的机制来解决这个问题。常用的中间件有redux-thunk
和dedux-saga
。
一、Redux中的中间件
首先,我们来看一下,Redux中的中间件是什么?如下图:
React中的中间件位于Action和Store之间,中间件是对
dispatch
方法的封装和升级。下面我们就以处理异步请求的操作来演示Redux中间件的使用方式。对于处理异步操作,我们常用的中间件有
redux-thunk
和dedux-saga
。至于两个中间件的差别,我们在具体讲解完这两个中间件后在进行说明。
二、Redux中的中间件——Redux-chunk
在之前文中《React数据管理工具Redux流程分析及基础使用实践》,我们在/src/store/actionCreators.js
中定义action
的时候,要求各函数最后返回的必须是一个对象,但是通过中间件redux-chunk
的处理,可以允许actionCreators
返回函数,我们可以在这个函数中执行我们的异步代码,执行完成之后返回对象。具体流程如下:
1、安装Redux-chunk
yarn add redux-thunk
2、配置Redux-chunk环境
/src/store/index.js
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import reducer from './reducer'
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose
const enhancer = composeEnhancers(
applyMiddleware(thunk)
)
const store = createStore(reducer, enhancer)
export default store
此配置过程,可以查文档,也可以直接copy此处代码。
3、设置返回值为函数的antion
/src/store/actionCreators.js
这块是redux-thunk
的核心,也是我们最应该注意的地方。
import { INIT_LIST_ACTION } from './actionTypes'
export const initListAction = (data) => ({
type: INIT_LIST_ACTION,
data
})
export const getTodoList = () => {
return (dispatch) => {
setTimeout(function(){
let data = ['learning react', 'learning redux']
const action = initListAction(data)
dispatch(action)
})
}
}
/src/store/actionTypes.js
export const INIT_LIST_ACTION = 'init_list_action'
4、在reducer中处理数据
import { INIT_LIST_ACTION } from './actionTypes'
export default (state = defaultState, action) => {
...
if(action.type === INIT_LIST_ACTION){
const newState = JSON.parse(JSON.stringify(state))
newState.list = action.data
return newState
}
...
}
5、在组件中componentDidMount钩子函数中调用该异步请求
/src/TodoList.js
componentDidMount(){
const action = getTodoList()
store.dispatch(action)
}
三、Redux中的中间件——Redux-saga
我们在使用Redux-thunk
中间件的时候,是将异步的操作放在ActionCreators
中的。而Redux-saga
是将异步操作抽离到单独的文件中,通过调用generator函数执行。
1、安装Redux-saga
yarn add redux-thunk
2、配置Redux-saga环境
/src/store/index.js
import { createStore, applyMiddleware, compose } from 'redux'
import reducer from './reducer'
import createSagaMiddleware from 'redux-saga'
import todoSagas from './sagas'
const sagaMiddleware = createSagaMiddleware()
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose
const enhancer = composeEnhancers(
applyMiddleware(sagaMiddleware)
)
const store = createStore(reducer, enhancer)
sagaMiddleware.run(todoSagas)
export default store
3、创建saga文件
import { takeEvery, put } from 'redux-saga/effects'
import { GET_INIT_LIST } from './actionTypes'
import { initListAction } from './actionCreator'
function* getInitList(){
try {
const data = ['learning react','learning redux']
const action = initListAction(data)
yield put(action)
} catch (e) {
console.log('network error')
}
}
function* mySaga(){
yield takeEvery(GET_INIT_LIST, getInitList)
}
export default mySaga
4、定义actionCreators中的action
/src/store/actionCreators.js
export const getInitList = (data) => ({
type: GET_INIT_LIST
})
5、创建actionTypes类型
export const GET_INIT_LIST = 'get_init_list'
6、使用Redux-saga
/src/TodoList.js
import { getInitList } from './store/actionCreator'
componentDidMount(){
const action = getInitList()
store.dispatch(action)
}
使用Redux-saga,在组件中通过store.dispatch(action)
提交action后,会在/src/store/sagas.js
中的generator函数中监测到,我们在在sagas文件中完成我们的异步请求操作。在经过sagas处理之后,我们就可以在reducer中继续执行我们熟悉的操作了——修改数据。
import { GET_INIT_LIST } from './actionTypes'
export default (state = defaultState, action) => {
...
if(action.type === GET_INIT_LIST){
const newState = JSON.parse(JSON.stringify(state))
newState.list = action.data
return newState
}
...
}
项目目录结构如下图:
项目目录结构
Redux-saga和Redux-thunk解决的问题一致,但是实现方式有所不同,saga把异步请求的操作全部放在saga文件中,而thunk只是来原来的基础上,对actionCreator进行一些操作(执行函数)。一般来说,saga用于大型复杂项目,其API很多,拓展性极好。
网友评论