刚来公司的时候,对react项目中的thunk中间件的作用一直不太了解,最近有时间决定好好研究一下。鉴于本人初次写博客,并已假设读者已掌握redux的一些基本用法;如有错误,还望指出。不胜感激!
首先简单回顾一下redux工作流程
图画的不太好,见谅;
对于reactUI组件来说,数据的来源无外乎两种,一种是用户主动触发的动作,例如点击事件、提交表单,输入操作;另一种是组件主动的数据更新,如获取页面初始数据,子组件接受父组件的props变化而进行更新视图操作;
如图所示,无论那种对于数据的操作,对于view都会派发出一个action
状态的更新
正如我们所知,在redux里,每次更新后的Store都会对应着一个新的view,而Store里面数据的更新依赖action的触发————Store.dispatch(action)会自执行初始化中createStore中注入的reducers,从而计算出新的状态。
import { createStore } from 'redux' //reducer 计算状态的纯函数 //initialState 初始化数据 //enhancers中间件 createStore(reducers, initialState, enhancers)
action的使用和插件的扩展
对于组件的输入操作(如点击事件),可以将store.dispatch(action)绑定到组件
const store = createStore(reducer); const TodoList = ({ state, someActionCreator }) => ( <ul> {state.map(someState => <Todo key={someState.someData} onClick={() => store.dispatch(someActionCreator(state.someData))} /> </ul> )
或者通过connect方法,从组件的props中拿到dispatch方法,发出一个action
// 将state注入到组件的props里 // 注意,这里的state指的是redux管理的数据,每一个view的状态对应着 // 唯一的state; // state的集合就是redux管理的store const mapStateToProps = store => ({ state: store.state }) // 将action注入到组件的props 里 const mapDispatchToProps = dispatch => ({ actions: state => dispatch(actionCreators(state)) }) export default connect( mapStateToProps, mapDispatchToProps )(TodoList)
然后组件绑定事件就可以改成这样 ,( actionCreators用于生成action, 参考官方链接 https://redux.js.org/basics/actions)
const TodoList = ({ state, actions }) => ( `<ul> {state.map(someState => <Todo key={someState.someData} onClick={() => actions(someState.someData)} /> </ul>` )
那么问题来了,dispatch是同步执行reducers生成新状态的,对于页面的操作没有问题;但是如果点击事件是请求了某个结果,需要等待结果响应后再更新视图呢?应该如何处理?
因而redux引入了thunk中间件,对action进行了扩展
##thunk中间件解决了什么问题?
引入thunk插件后,我们可以在actionCreators内部编写逻辑,处理请求结果。而不只是单纯的返回一个action对象。
//未引入前的写法 let nextTodoId = 0 export const addTodo = text => ({ type: 'ADD_TODO', id: nextTodoId++, text }) //引入thunk后 let nextTodoId = 0 export const addTodo = text => ({ return async dispatch => { //dosomething, request await request() dispatch({ type: 'ADD_TODO', id: nextTodoId++, text }) } })
thunk中间件的使用方法
import { applyMiddleware, createStore } from 'redux'; import thunk from 'redux-thunk'; const store = createStore( reducer, applyMiddleware(thunk) );
createStore其实可以接受三个参数,第二个参数preloadedState一般作为整个应用的初始化数据,如果传入了这个参数,applyMiddleware就会被当做第三个参数处理
const store = createStore( reducer, initialState, applyMiddleware(thunk) );
中间件都要放到applyMiddleware里,如果要添加中间件,可以依次添加,但是要遵循文档定义的顺序
const store = createStore( reducer, initialState, applyMiddleware(thunk,middleware1, middleware2) );
源码解读
也许你会奇怪,为什么使用的时候要按照上面的写法,那我们就一起看下方法的实现
首先是createStore的参数顺序
function createStore(reducer, preloadedState, enhancer) { var _ref2; if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { enhancer = preloadedState; preloadedState = undefined; } if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.'); } return enhancer(createStore)(reducer, preloadedState); } if (typeof reducer !== 'function') { throw new Error('Expected the reducer to be a function.'); }
第一个判断已经告诉了我们答案,参数的类型检验结果决定了顺序