在Redux架构中,reducer是一个纯函数,它的职责是根据previousState和action计算出新的state。在复杂应用中,Redux提供的combineReducers让咱们能够把顶层的reducer拆分红多个小的reducer,分别独立地操做state树的不一样部分。而在一个应用中,不少小粒度的reducer每每有不少重复的逻辑,那么对于这些reducer,如何抽取公共逻辑,减小代码冗余呢?这种状况下,使用高阶reducer是一种较好的解决方案redux
咱们将顶层的reduce拆分红多个小的reducer,确定会碰到reducer复用问题。例若有A和B两个模块,它们的UI部分类似,此时能够经过配置不一样的props来区别它们。那么这种状况下,A和B模块能不能共用一个reducer呢?答案是否认的。咱们先来看一个简单reducer:架构
const LOAD_DATA = 'LOAD_DATA'; const initialState = { ... }; function loadData() { return { type: LOAD_DATA, ... }; } function reducer(state = initialState, action) { switch(action.type) { case LOAD_DATA: return { ...state, data: action.payload }; default: return state; } }
若是咱们将这个reducer绑定到A和B两个不一样模块,形成的问题将会是,当A模块调用loadData来分发相应的action时,A和B的reducer都会处理这个action,而后A和B的内容就彻底一致了。函数
这里咱们必需意识到,在一个应用中,不一样模块间的actionType必须是全局惟一的。spa
所以,要解决actionType惟一的问题,还有一个方法就是经过添加前缀的方式来作到:code
function generateReducer(prefix, state) { const LOAD_DATA = prefix + 'LOAD_DATA'; const initialState = { ...state, ...}; return function reducer(state = initialState, action) { switch(action.type) { case LOAD_DATA: return { ...state, data: action.payload }; default: return state; } } }
这样只要A和B模块分别调用generateReducer来生成相应的reducer,就能解决reducer复用的问题了。而对于prefix,咱们能够根据本身的项目结构来决定,例如${页面名称}_${模块名称}。只要可以保证全局惟一性,就能够写成一种前缀。it
除了解决复用问题,高阶reducer的另外一个重要做用就是对原始的reducer进行加强。redux-undo就是典型的利用高阶reducer来加强reducer的例子,它主要做用是使任意reducer变成能够执行撤销和重作的全新reducer。咱们来看看它的核心代码实现:io
function undoable(reducer) { const initialState = { // 记录过去的state past: [], // 以一个空的action调用reducer来产生当前值的初始值 present: reducer(undefined, {}), // 记录后续的state future: [] }; return function(state = initialState, action) { const { past, present, future } = state; switch(action.type) { case '@@redux-undo/UNDO': const previous = past[past.length - 1]; const newPast = past.slice(0, past.length - 1); return { past: newPast, present: previous, future: [ present, ...future ] }; case '@@redux-undo/REDO': const next = future[0]; const newFuture = future.slice(1); return { past: [ ...past, present ], present: next, future: newFuture }; default: // 将其余action委托给原始的reducer处理 const newPresent = reducer(present, action); if(present === newPresent) { return state; } return { past: [ ...past, present ], present: newPresent, future: [] }; } }; }
有了这高阶reducer,就能够对任意一个reducer进行封装:ast
import { createStore } from 'redux'; function todos(state = [], action) { switch(action.type) { case: 'ADD_TODO': // ... } } const undoableTodos = undoable(todos); const store = createStore(undoableTodos); store.dispatch({ type: 'ADD_TODO', text: 'Use Redux' }); store.dispatch({ type: 'ADD_TODO', text: 'Implement Undo' }); store.dispatch({ type: '@@redux-undo/UNDO' });
查看高阶reducer undoable的实现代码能够发现,高阶reducer主要经过下面3点来加强reducer:
可以处理额外的action;
可以维护更多的state;
将不能处理的action委托给原始reducer处理。function