Skip to content

redux-thunk源码解读 #4

Open
@Juliiii

Description

@Juliiii

背景

最近刚把redux的源码阅读放了上来,那么再接再厉,咱们来看个redux中间件的源码,随便学习下怎么写个redux的中间件。额,为了简单点,所以选了redux-thunk。redux-thunk是什么?用过的同学应该知道是,redux处理异步action的一个中间件。然而他的源码也非常简单,不到20行,就完全可以实现处理异步action的需求,再一次感叹大牛的奇思妙想。

开始

  • 小二,上代码!

    const thunk = createThunkMiddleware();
    thunk.withExtraArgument = createThunkMiddleware;
    
    export default thunk;

    先看看它的导出,哦,是先执行了createThunkMiddleware 这个函数,得到一个返回值,并将返回值赋值给thunk,并同时将这个函数挂载在thunk 上。最后导出给用户使用。那createThunkMiddleware 就是我们要剖析的核心了。下面为代码:

    function createThunkMiddleware(extraArgument) {
      return ({ dispatch, getState }) => next => action => {
        if (typeof action === 'function') {
          return action(dispatch, getState, extraArgument);
        }
    
        return next(action);
      };
    }

    这个函数,接受extraArgument 作为参数,并返回一个函数,而这个函数就是我们之前聊redux时,说到中间件的写法。就是形如 store => next => action => {}。所以这个函数会返回一个中间件给我们。而这个中间件,会判断action是不是function,如果是,就传入dispatch,getState等,执行,并返回;如果不是,就过,传给下一个中间件 。好了,redux-thunk的原理就这么简单。哈哈哈哈哈,额。为了避免有的同学没用过redux-thunk,那下面结合一个例子来讲解为毛原理就这么简单

    async function getUserInfo(dispatch, getState, userId) {
        try {
            const user = await axios.get(`/users/${userId}`);
            dispatch({
                type: 'GETUSERINFO_SUCCESS',
                data: user
            });
        } catch(e) {
            dispatch({
                type: 'GETUSERINFO_FAILED',
                error: e
            });        
        }
    }
    
    store.dispatch(getUserInfo);

    redux-thunk的用法,如果我没记错,应该是这么用(毕竟一直用redux-saga),正常的dispatch是接受一个简单对象的。但是redux-thunk允许我们传入一个函数,这个函数就是一个异步操作的函数。redux-thunk检测到是个函数的时候,就会执行该函数。函数会做异步操作。在未来的某个时间点,异步操作完成,会根据成功或者失败,去dispatch不同的action,而这次dispatch的action就是一个正常的action,即为一个简单对象,和正常使用redux的效果是一样的。所以,大家明白了redux的内部原理了吧。

  • 那么看了上面,懂得写一个中间件的套路了吗?

    看了redux和redux-thunk的源码后,我个人总结出一个redux的中间件应该具备以下两点

    • 形如 store => next => action => { };
    • {} 这部分要调用 next(action);

    第一点是因为,在redux的applyMiddleware中,对于中间件的处理,是先传入一个middlewareAPI,这个API包含了两样东西,一个是store.getState,一个是增强过的dispatch。然后得到一个形如 next => action => {} 的数组。然后对于这个数组,才有compose函数来处理,另其变成 (...args) => f(g(h(...args)))的一个函数。再然后再传入一个store.dispatch,这个函数就会执行,然后这个数组的函数就会从右往左执行,最后得到第一个action => {}的函数。当dispatch的时候,这个函数被执行,由于这个函数内调用了 next(action),所以中间件们就会以一种链式的结构被执行。所以个中间件只要具备以上两点,个人觉得就可以了。举了例子

    const logger = store => next => action => {
        console.log('currentState: ', store.getState());
        let result = next(action)
        console.log('nextState', store.getState());
        return result
    }

    这就是一个logger的中间件了,咱们看下效果:

    logger的demo

结语

这次也到这里了,这次篇幅不多,难度不难。大家也可以自行看看源码。老规矩,源码地址:https://github.com/Juliiii/source-plan 欢迎大家star或者fork也可以和我讨论学习

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions