React 学习系列(三): Redux 异步解析

2,849 阅读3分钟

Redux 同步操作

Redux 的基本用法如下所示:

在上面的流程中, 用户通过 dispatch 发起 Action 后, reducer 会立即计算返回新的 state, 这是一个 同步 的过程。 但是如果我们想在 Action 发起之后, 过一段时间再执行 reducer 计算 state, 即 异步计算 state, 该如何操作呢?

Redux 异步操作

Redux 中, Action对象 只是一个简单的 js对象, 用于表达用户想要修改 state 的意图, Reducer 也是一个 js 纯函数, 只负责 根据 Action 对象计算 state, 不会进行 API 请求和路由跳转。那么,我们只能在发送 Action 的时候做些文章, 即对 dispatch 方法做改造, 使得 dispatch 方法可以进行 异步操作

redux-thunk 是供 redux 使用的一个 中间件(middleware),这个中间件会对 store对象 原生的 dispatch 方法 进行包装, 然后返回一个 新的dispatch方法。 我们给这个 新的dispatch方法 传入一个函数, 即 dispatch(func), 在这个函数中我们执行 异步操作,然后在 异步操作的回调方法中执行 原生的dispatch(action) 操作, 修改 state

    import { createStore, applyMiddleware } from 'redux'
    import thunk from 'redux-thunk'
    
    // Action构造函数, 返回一个 Action 对象
    const Action = text => {
        value: text
    }
    
    // Action构建函数, 返回一个 thunk 函数
    const post = () => dispatch => {
        // 异步操作
        setTimeout(() =>{
            dispatch(Action('123'))
        }, 2000)
    }
    
    // reducer
    const reducer = (state, action) {
        ...
        return state;
    }
    
    // 中间件列表
    const middle = [thunk]
    
    // 构建 Store 对象, 并应用中间件
    const store = createStore(reducer, applyMiddleware(...middle))
    
    // 同步操作
    store.dispatch(Action('123'));
    
    // 异步操作
    store.dispatch(post())
    

Redux 异步解析

redux-thunk:

    // redux 通过 applyMiddleware 使用 redux-thunk时, 会先执行 createThunkMiddleware 方法
    // 传入 redux 原生的 dispatch、 getState 方法
    // 或者传入上一个中间件包装以后的 dispatch、getState
   function createThunkMiddleware(extraArgument) {
     return ({ dispatch, getState }) => next => action => {
       // 如果action是函数,即thunk函数, 直接执行thunk函数
       if (typeof action === 'function') {
         return action(dispatch, getState, extraArgument);
       }
  
       // 如果acton不是函数, 调用原生的dispatch方法派发Action, 修改state
       return next(action);
     };
   }
   
   const thunk = createThunkMiddleware();
   thunk.withExtraArgument = createThunkMiddleware;
   
   export default thunk

applyMiddleware:

    export default function applyMiddleware(...middlewares) {
    
      // 返回enhancer, createStore => createStore
      return (createStore) => (reducer, preloadedState, enhancer) => {
      
        // 执行 redux 原生的 createStore 方法,构建一个 store 对象
        const store = createStore(reducer, preloadedState, enhancer)
        // 原生的 dispatch 方法
        let dispatch = store.dispatch
        let chain = []
    
        const middlewareAPI = {
          getState: store.getState,
          // 包装以后的 dispatch 方法
          dispatch: (action) => dispatch(action)
        }
        
        // [chainA, chainB, chainC, chainD], 格式为 next => action
        chain = middlewares.map(middleware => middleware(middlewareAPI))
        
        // 先通过 compose 方法处理 chain,结果为 chainA(chainB(chainC(chainD(store.dispatch))))
        // dispatch 是包装以后的dispatch方法
        dispatch = compose(...chain)(store.dispatch)
        
        // 返回一个新的Store对象, dispatch方法被重新包装
        return {
          ...store,
          dispatch
        }
      }
    }

通过 applyMiddleware方法 和 thunk 中间件, store 对象的原生 dispatch 方法会被包装成如下形式:

    // 包装后的 dispatch 方法
    function (action) {
        if (typeof action === 'function') {
            return action(dispatch, getState, extraArgument);
        }
        return dispatch(action);
    }
    

store.dispatch(Action('123')), 我们调用的是 包装后的 dispatch方法, 因为 Action('123') 返回的是 对象即不是函数, 所以 新的dispatch方法 会直接 调用 store 原生的 dispatch 方法,然后 派发 Action触发 state 的修改

store.dispatch(post()), 我们调用的同样是 包装后的dispatch方法post() 返回的是 函数即 thunk 函数, 所以 thunk函数自动执行。在 thunk函数 中, 我们可以执行 异步操作, 然后在 异步操作回调方法 中调用 store 的原生 dispatch 方法派发 Action触发 state 的修改

总结一下, Redux 的异步操作流程如下: