redux系列之react-thunk源码解读
前言
我们知道,redux提供了一套很好的状态管理机制,通过在createStore方法创建出store对象,通过store.getState获取状态对象,通过reducer定义出action改变对应的state变化的规则,通过replaceReducer动态改变reducer规则,结合dispatch一个action来修改状态对象state,甚至,redux还提供了applyMiddleware来给redux增加中间件,提供dispatch和getState2个方法给中间件使用;
不过,redux只是一个和其他库无关的状态管理库,为了能够方便的在react中使用,我们必须结合react-redux来使用。react-redux通过Provider使用给react项目的所有子组件注入store,通过connect给组件注入state和dispatch。
但是,有了react-redux还不够!我们还需要react-thunk!
为什么需要react-thunk?
ajax请求发出去的时候,经常涉及到这3个状态:loading,success,error3个状态,而且这3个状态是在同一个地方产生的,那么,如果仅有react-thunk,那么,我们需要在组件内部这样写代码:
this.props.dispatch(loadingAction); ajax('/getData').then(res => { this.props.dispatch(successAction); }).catch(err => { this.props.dispatch(errorAction); })
如果上述代码在组件内多次被调用,就需要封装成函数,getData,如果要夸页面调,那就需要封装到专门的api请求里面去,代码如下:
//api.js export function getData(dispatch) { this.props.dispatch(loadingAction); ajax('/getData').then(res => { this.props.dispatch(successAction); }).catch(err => { this.props.dispatch(errorAction); }); } //index.js业务组件代码 import {getData} from './api.js'; class MyComponent extends Component { handleClick() { getData(this.props.dispatch); } }
并且,如果getData内部需要用到全局的state,那么,我们必须要从业务代码里面传入getState函数,那么代码就会变成下面这样:
//api.js export function getData(dispatch, getState) { const state = getState(); dispatch(loadingAction); ajax('/getData').then(res => { dispatch(successAction); }).catch(err => { dispatch(errorAction); }); } //index.js业务组件代码 import {getData} from './api.js'; class MyComponent extends Component { handleClick() { getData(this.props.dispatch, this.props.store.getState); } }
似乎这样也行,但是,总感觉不对,每次都要从业务组件里面显式的传入2个参数dispatch和getState,这样感觉很烦。
所以呢,通过以上分析,我们react-thunk就闪亮登场了!
react-thunk怎么用?
//store.js import thunk from 'redux-thunk'; const initialState = {}; const reducer = function(state={}, action){ return state }; const store = createStore(reducer, initialState, applyMiddleware(thunk)); //action.js export function getData(dispatch, getState) { const state = getState(); dispatch(loadingAction); ajax('/getData').then(res => { dispatch(successAction); }).catch(err => { dispatch(errorAction); }); } //index.js业务组件代码 import { getData } from './actions.js'; class MyComponent extends Component { handleClick() { getData(); } }
这样,使用了react-thunk之后,我们无需从getData中传入dispatch和getState,而是中间件会默认帮我们做这个事情,这样,我们就可以在一个函数内部dispatch多个action,以及随意的拿到全局的state
react-thunk怎么实现的?
看源码:
//一个创建thunk的中间件工厂函数 function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); }; } //创建出thunk中间件 const thunk = createThunkMiddleware(); //把工厂函数附属到thunk对象上,方便高级用户自定义传入更多的参数到action中去 thunk.withExtraArgument = createThunkMiddleware; export default thunk;
其实,就是一个中间件的写法,当发现action是一个函数的时候,就会传入dispatch和getState这2个参数,而这2个参数是从中间件那里传过来的。
全文完