React 实践:改造 Redux action generator,编写 Redux reducer generator (2)

Author Avatar
Splendour 2月 16, 2017

背景

接上一篇博文,我们来探讨一下怎么通过配置来生成 reducer,以及如何往自动生成的 reducer 中增加自定义的转换(action 触发 reducer 变化)

reducer 的组成

  • 在上一篇博文中,我们通过 action 的配置,自动生成了一些 reducer,这一部分我们称为来自 action 配置的 reducer
  • 我们自定义的 reducer,需要定义名字以及它对应的通过 action 之后的转换(nextState)
  • 由 action 配置自动生成的 reducer,我们往其中增加一些转换规则

完善 reducer_generator

我们可以把 reducer 的配置分为两部分:basics(自定义 reducer) 和 extras(往自动生成的 reducer 中添加规则)
basics 和 extras 的配置规则差不多,不同的是 basics 的每个元素多了一个 initialState 属性,如下例子所示

[
  {
    name: 'globalLoading',
    initialState: {
      show: false,
      msg: '
    },
    mappers: [
      {
        action: 'showLoading',
        nextState: (state, action) => ({
          show: true,
          msg: action.data || 'loading'
        })
      },
      {
        action: 'hideLoading',
        nextState: (state, action) => ({
          show: false,
          msg: ''
        })
      }
    ]
  }
]

和我们原来写 reducer 的方式很像,只不过变成了配置,简化了代码。
最终的 reducer 通过这个函数暴露出去

export default (prefix, actionConfigs, reducerConfigs) => {
  const result = {};

  forEach(action =>
    addReducersFromAction(prefix, action, result, reducerConfigs.extras || [], actionLoadingList)
  )(actionConfigs);

  forEach(reducer =>
    addReducersFromBasicConfigs(prefix, reducer, result)
  )(reducerConfigs.basics || []);

  return result;
};

自定义 reducer

其实也就是实现 addReducersFromBasicConfigs 函数,很简单,代码如下

const addReducersFromBasicConfigs = (prefix, reducer, result) => {
  const {
    name, initialState, mappers
  } = reducer;

  result[`${name}`] = (state = initialState, action) => {
    for (let i = 0, length = mappers.length; i < length; i ++) {
      const mapper = mappers[i];
      if (action.type === getAction(prefix, `${mapper.action}`)) {
        return mapper.nextState(state, action);
      }
    }
    return state;
  };
};

补充自动生成的 reducer 规则

我们在上一篇博文已经看到了,对于每一种类型的 action,都有对应的 reducer_generator 代码。
为了实现规则的补充,我们让自动生成的 reducer 在经过自动生成的规则的判断之后,返回一个函数。
比如 loading 的代码,我们修改如下

result[`${name}Loading`] = (state = false, action) => {
  if (action.type === getAction(prefix, name, defines.ACTION_STATUS_LOAD)) {
    return true;
  }
  if (action.type === getAction(prefix, name, defines.ACTION_STATUS_SUCCESS)) {
    return false;
  }
  if (action.type === getAction(prefix, name, defines.ACTION_STATUS_FAIL)) {
    return false;
  }
  return getExtraMapper(prefix, `${name}Loading`, state, action, extras);
};

其中,getExtraMapper 函数就是我们处理自定义规则的地方。该函数代码如下

const getExtraMapper = (prefix, name, state, action, extras) => {
  for (let i = 0, length = extras.length; i < length; i ++) {
    const extra = extras[i];
    if (extra.name === name) {
      for (let j = 0, mappersLength = extra.mappers.length; j < mappersLength; j ++) {
        const mapper = extra.mappers[j];
        if (action.type === getAction(prefix, mapper.action)) {
          return mapper.nextState(state, action);
        }
      }
    }
  }
  return state;
};

思路很简单,就是遍历 extras 的配置,从中寻找匹配的 name,然后将规则添加进去,是不是很简单?

总结

至此,我们就实现了通过配置生成所有的 action 和 reducer 的方法,让项目代码量骤减,开发效率也提高了不少。