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个参数是从中间件那里传过来的。

全文完

相关推荐