redux-actions 原理及实现

491 阅读3分钟

redux-actions是个工具库,为了方便编写 redux 的 action 和 reducer 逻辑
提供更便捷的异步操作,它跟 react、redux 是解耦的

核心函数

  • createActions
  • handleActions
  • combineActions

handleActioncreateAction 这里没有单独实现,其实就是handleActionscreateActions函数拆分出的逻辑提供给外部而已。

学习redux-actions的原理之前,需要了解redux的基本概念 reduceractionactionCreator

原理部分只提供简化的代码,不是源码解析!

可以去 👉 仓库传送门 进行调试,直接copy出来运行即可

用法

官方示例:

import { createActions, handleActions, combineActions } from "redux-actions";

const defaultState = { counter: 10 };

const { increment, decrement } = createActions({
  INCREMENT: (amount = 1) => ({ amount }), // type: (...args) => payload
  DECREMENT: (amount = 1) => ({ amount: -amount }),
});

const reducer = handleActions(
  {
    [combineActions(increment, decrement)]: (
      state,
      { payload: { amount } }
    ) => {
      return { ...state, counter: state.counter + amount };
    },
  },
  defaultState
);

export default reducer;

原理

1. 工具函数

源码中提供的工具函数,用到的比较多,所以提出来写在这里 用于把数组项当作 key callback 的返回值当作 value 构造一个对象

function arrayToObject(array, callback) {
  return array.reduce((patialObject, curr) => callback(patialObject, curr), {});
}

arrayToObject(["a", "b"], (patialObject, key) => ({
  ...patialObject,
  [key]: (val) => val,
})); // {a: (val) => val, b: (val) => val}

2. createActions

action 的结构是非常简单的,就是 redux 中定义的格式 {type: string, payload: any}

actionCreator 就是返回 action 的函数,也是 redux 中的概念。

createActions 的原理就是根据 redux-actions 定义的API,转换成 actionCreator

核心原理一个函数就搞定 👇👇👇

⚠️ [flag1] 源码中修改了 actionCreator 的 toString 方法,返回 type 的 string 值,这里就省略这一步,可以去顶部分享的代码仓库中查看这个实现

/**
 * type -> payloadCreator
 * 
 * createActions({
 *  INCREMENT: (amount = 1) => ({ amount }),
 *  DECREMENT: (amount = 1) => ({ amount: -amount })
 * })
 *
 * 会生成下面的 actionCreators 👇👇👇
 *
 * actionCreators: {
 *   increment: (amount) => ({
 *      type: "INCREMENT",
 *      payload: { amount }
 *   }),
 *   decrement: () => ({
 *      type: "DECREMENT",
 *      payload: { amount: -amount }
 *   })
 * }
 *
 * 可以通过 dispatch 来触发:
 * dispatch(actionCreators.increment(3))
 * dispatch(actionCreators.decrement(3))
 *
 * 极简化版 createActions
 */
function createActions(actionMap) {
  return arrayToObject(Object.keys(actionMap), (patialObject, type) => ({
    ...patialObject,
    [type.toLowerCase()]: function actionCreator() {  // 源码中对这个函数的 toString 做了处理,会返回 type.toLowerCase() 的值
      return {
        type,
        payload: actionMap[key](...args),
      };
    },
  }));
}

3. handleActions

handleActions 返回一个 reducer

原理很简单 👇👇👇,这是最基础的原理,下面还有一个实现是这个的变形。

/**
 * handleActions({
 *    [increment]: (state, { payload }) => ({ ...state, count: state.count + payload.count }),
 *    [decrement]: (state, { payload }) => ({ ...state, count: state.count + payload.count }),
 * }, {})
 */
function handleActions(handlers, defaultState) {
  // 源码中支持嵌套,会打平 handlers
  return (state = defaultState, { type, payload }) =>
    handlers[type.toString()](state, payload);
}

实际源码中,handlers 兼容了 Map,并且支持嵌套,会有一个打平的操作,根据 handlers 的结构自动给type加上命名空间

4. combineActions

还可以再简化一点!!套娃就完事了。

对这个例子来说,在我们定义的 action 下,incrementdecrement 的函数体是一样的,那就合并它!

handleActions({
    [increment]: (state, { payload }) => ({...state, count: state.count + payload.count }),
    [decrement]: (state, { payload }) => ({ ..state, count: state.count + payload.count,
    })},
  {}
);

// 🌟 改成这样,也就是官方示例中的样子
handleActions({
      [combineActions(increment, decrement)]: (state, { payload }) => ({...state, count: state.count + payload.count })
    },
  {}
);

combineActions 就是单纯把传入的多个参数通过连接符连接起来返回,要使用合并的功能,还需要对handleActions进行更改,当传入的type 可以命中到 handleActionskey 值规则时调用对应的reducer

// actionsTypes.map(toString) 就调用了 actionCreator 的 toString方法,上面 [flag1] 提到过会返回 type 值
function combineActions(...actionsTypes) {
  return actionsTypes.map(toString).join("||")
}

// 改动一下handleActions
function handleActions(handlers, defaultState) {
  // 把 reducerMap 转化为数组,通过闭包保存 reducer
  const reducers = Object.keys(handlers).map(type => {
    const types = type.toString().split("||")
    const reducer = handlers[type.toString()]

    return (state, action) => {
      if(~types.indexOf(action.type)) {
        // 🌰 'INCREMENT' 会命中 'INCREMENT || DECREMENT' 对应的 reducer
        return reducer(state, action)
      } else {
        // 没有命中,state 原样返回
        return state
      }
    }
  })
  
  // 🌟 reducer 的执行入口
  return (state = defaultState, action) => 
    reducers.reduce((prevState, reducer) => reducer(prevState, action), state)
}