Redux-thunk快速入门

11,743 阅读4分钟

前言

最近刚刚完成了毕业答辩,我的毕设内容是基于React系列技术栈开发的一个类似Instagram的Web App,戳此看看。开发完后,我惊奇的发现:咦,之前就听说有个叫做redux-thunk的东西,我怎么没用到?业务场景太简单了?于是大概研究了下。。

概念的基本介绍

关于redux-thunk的基本介绍,也许你可以先看看stackoverflow上面的介绍。个人理解:redux-thunk改写了dispatch API,使其具备接受一个函数作为参数的能力,从而达到middleware的效果,即在redux的dispatch action => reducer => store这个流程中,在action 被发起之后,到达 reducer 之前的扩展点,加入相关操作,比如发生请求、log信息等。

实际使用

以我本次毕设项目中,在redux流程中加入异步请求为例,为动态点赞功能部分代码实现:

  • 本来我是这样写的
//from action.js
const LIKE = (id) => ({
     type: "LIKE",
     id:id
})

reqLike({id:id}).then(res =>{ dispatch(LIKE(id))})

可以看到,我在请求以后的回调函数中dispatch action去同步redux store中的状态。

  • 加入redux-thunk之后我是这样写的:
//from action.js
const LIKE = (id) => {
    return function (dispatch,getState) {
        reqLike({id:id}).then(res =>{ 
            dispatch({
                type: "LIKE",
                id:id
            })
        })
    }
}

dispatch(LIKE(id))

改变以后,从功能层面上来说,两者并无差别,都可以满足业务场景需求。但除此之外我们可以发现:

  • 1.dispatch接受的参数由一个PlainObject变为一个函数
  • 2.我们把请求的异步操作从dispatch action这个redux流程外塞到的流程里,这看起来将异步操作内聚到这个流程中,无论是从逻辑上理解(这很middleware!)还是项目代码开发维护(区分异步与同步状态管理流程进行维护管理)上都是很大的改进
  • 3.如果项目中有多处需要实现点赞功能,我们可以节省很多冗余代码,不用到处在dispatch外层套上reqLike(id).then(...)

源码解析

了解了redux-thunk的基本概念以及应用后,我们一起看看源码加深下理解吧,源码十分精巧。

首先看到redux源码中applyMiddleware的部分,我们将thunk作为参数传入之后,直接返回了一个函数,这个函数作为enhancer传入redux源码中的createStore函数中。

export default function applyMiddleware(...middlewares) {
  //这个返回函数就是enhancer
  return (createStore) => (reducer, preloadedState, enhancer) => {
    const store = createStore(reducer, preloadedState, enhancer)
    let dispatch = store.dispatch
    let chain = []

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

在redux源码中的createStore函数中,enhancer被执行,传入参数createStore,又紧接着执行其返回的函数,传入reducer和preloadedState.

if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    
    return enhancer(createStore)(reducer, preloadedState)
}

接下来,我们进入applyMiddleware和thunk的关键部分,上面applyMiddleware接受的最初的(...middlewares)参数其实就是thunk,thunk会被执行,并且传入参数getState和dispatch.

//传入到thunk的参数
const middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
//在map中执行thunk
chain = middlewares.map(middleware =>middleware(middlewareAPI))
//重新改写dispatch
dispatch = compose(...chain)(store.dispatch)

那么上面的chain是什么呢,我们终于可以去看redux-thunk的源码了!

function createThunkMiddleware(extraArgument) {
  return function (_ref) {
    var dispatch = _ref.dispatch,
        getState = _ref.getState;
    //这里返回的函数就是chain
    return function (next) {
    //这里返回的函数就是改写的dispatch
      return function (action) {
        if (typeof action === 'function') {
          return action(dispatch, getState, extraArgument);
        }

        return next(action);
      };
    };
  };
}

var thunk = createThunkMiddleware();

从源码我们可以看出,chain就是以next作为形参的匿名函数,至于compose只是不断传递每个函数的返回值给下一个执行函数,然后依次去执行它所有传入的函数而已,它源码中的注释说的很清楚:For example, compose(f, g, h) is identical to doing (...args) => f(g(h(...args))).

我们这里的chain只是一个函数而已,所以很简单,就是执行chain,并且传入store.dispatch作为next就行。

接下来,进入最后一步,改写了dispatch,最终变为:

function (action) {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }
    //next为之前传入的store.dispatch,即改写前的dispatch
    return next(action);
};

如果传入的参数是函数,则执行函数。否则还是跟之前一样dispatch(PlainObject).

总结

redux-thunk实现了相关异步流程内聚到redux的流程中,实现middleware的功能,也便于项目的开发与维护,避免冗余代码。而实现的方式便是改写redux中的dispatch API,使其可以除PlainObject外,接受一个函数作为参数。