Redux 异步:
通常情况下,action 只是一个普通的对象,不能包含异步操作,这导致了很多创建 action 的逻辑只能写在组件中,代码量较多也不便于复用,组件的业务逻辑也不清晰,使用中间件了之后,可以通过actionCreator
异步编写action
,这样代码就会拆分到 actionCreator 中,可维护性大大提高,可以方便于测试、复用,同时 actionCreator 还集成了异步操作中不同的 action 派发机制,减少编码过程中的代码量。
常见的异步库:
- Redux-thunk
- Redux-saga
- Redux-effects
- Redux-side-effects
- Redux-loop
- Redux-observable
- …
基于Promise的异步库:
- Redux-promise
- Redux-promises
- Redux-simple-promise
- Redux-promise-middleware
最简单也是最常用的方法就是使用Redux Thunk
middleware,这样就能用更为复杂或者异步的逻辑书写 action 创建函数。另一个被广泛使用的方法是Redux Saga
,你可以用generator
书写类同步代码,就像在 Redux 应用中使用 “后台线程” 或者 “守护进程”。
redux-thunk
在终端中执行下面的命令,安装 redux-thunk
yarn add redux-thunk
创建 store 的时候:
- applyMiddleware 表示通知 redux 要用到一个异步的中间件;
- createStore() 中第二个参数传入 applyMiddleware(thunk);
/* store.js */
import {createStore,applyMiddleware} from 'redux'
import reducer from './reducer'
import thunk from 'redux-thunk'
const store=createStore(reducer,applyMiddleware(thunk))
export default store;
reducer.js文件:
- 根据 action 传过来的内容,对 store 中的数据做处理,并返回一个新的 state。
/* reducer.js */
const defaultState={ dataList:[] }
const reducer=(state=defaultState,action)=>{
switch(action.type){
case "ADD_LIST_DATA":
return{
dataList:list.concat(action.data)
}
default:
return state;
}
}
export default reducer;
connect.js文件中:
- 默认情况下,mapDispatch 返回的方法中,dispatch 中的 action 只能是一个扁平的对象。
- 使用
redux-thunk
中间件后,action
可以是一个函数
,在函数中进行异步操作,最终还是执行 dispatch。
/* connect.js */
import {connect} from 'react-redux'
import {loadListData} from './actionCreators'
const mapState=(state)=>{
return{ dataList:state.dataList }
}
const mapDispatch=(dispatch)=>{
return{
loadData:(params)=>{
dispatch(loadListData(params))
}
}
}
export default connect(mapState,mapDispatch)
actionCreators.js,请求接口,做异步操作
/* actionCreators.js */
const loadListData=(params)=>{
return(dispatch)=>{
axios.get('/datalist',params).then((rs)=>{
dispatch({type:"ADD_LIST_DATA",data:rs.data})
})
}
}
export { loadListData }
redux-saga
redux-saga
用的是ES6的生成器函数generator
功能,而不是async await
在终端中执行下面的命令,安装 redux-saga
yarn add redux-saga -S
创建 store 的时候:
- 使用 createSagaMiddleware() 创建 sagaMiddleware。
- mySaga 中是对数据做的异步处理,相当于thunk方式中 connect.js 和 actionCreators.js 中的操作。
/* store.js */
import {createStore,applyMiddleware} from 'redux'
import createSagaMiddleware from 'redux-saga'
import reducer from './reducer'
import mySaga from './sagas'
const sagaMiddleware = createSagaMiddleware()
const store = createStore(reducer,applyMiddleware(sagaMiddleware))
sagaMiddleware.run(mySaga)
export default store;
reducer.js文件:
- 根据 action 传过来的内容,对 store 中的数据做处理,并返回一个新的 state。
- 不管使用哪种异步库,reducer 里的操作都是一样的。
/* reducer.js */
const defaultState={ dataList:[] }
const reducer=(state=defaultState,action)=>{
switch(action.type){
case "ADD_LIST_DATA":
return{
dataList:list.concat(action.data)
}
default:
return state;
}
}
export default reducer;
connect.js文件中:
- dispatch 中 action 仍旧是一个扁平的对象,它的 type 属性会去匹配 takeEvery() 方法里的第一个参数。
/* connect.js */
import {connect} from 'react-redux'
const mapState=(state)=>{
return{ dataList:state.dataList }
}
const mapDispatch=(dispatch)=>{
return{
loadData:(params)=>{
dispatch({type:"async_loadListData",...params})
}
}
}
export default connect(mapState,mapDispatch)
sagas.js文件:
- takeEvery(),作用是将请求数据加到异步队列里,匹配 connect.js 中 dispatch 参数里的 type,匹配成功后,执行第二个参数,调用 put() 方法;
- put(),它的参数是 action,相当于 thunk 里的 dispatch。将 action 发送到 reducer 中处理,改变 state;
/* sagas.js */
import { put, takeEvery } from 'redux-saga/effects'
//请求接口,做异步操作
const loadListData=(params)=> axios.get('/apilist/datalist',params)
function* load(params) {
delete params.type;
let rs = yield loadListData(params);
yield put({type:"ADD_LIST_DATA",data:rs.data})
}
function* sagas(){
yield takeEvery('async_loadListData',load)
}
export default sagas;
redux-thunk 和 redux-saga 的区别
- 上面的案例中,异步处理数据,thunk方式是在actionCreators.js 文件中,saga 方式在 sagas.js文件里;
redux-saga
用的是generator
语法。- 异步操作时,在connect.js文件里,
thunk
方式,action是个函数
;saga
方式,action还是一个扁平的对象
,会根据这个名字去找takeEvery()
中的第一个参数,如果一致,则执行 takeEvery() 的第二个参数。 - saga方式,在 store.js 文件中配置好后,里面可以正常写;thunk方式,action 分扁平对象和函数。
- 在之前的项目中 thunk 方式用的比较多。