Reducer
Reducer 是一个纯函数,接收旧 State 和 Action,返回新的 State,即(previousState, action) => newState
。纯函数的意义在于相同的输入只会得到相同的输出,并且不会产生任何可观察的副作用,所以以下操作永远不要在 Reducer 中进行:
- 修改传入的参数;
- 执行有副作用的操作,例如执行 API 请求或者路由跳转。
- 调用其他非纯函数,例如即
Date.now()
或者即Math.random()
。
Redux 在首次执行时,State 为undefined,此时可以返回应用的初始 State。以下是一个 Reducer 的示例。
function contentApp(state = initialState, action) {
switch (action.type) {
case ADD_CONTENT:
return Object.assign({}, state, { content: action.payload.content });
case SET_FILTER:
return Object.assign({}, state, { filter: action.payload.filter });
default:
return state;
}
}
示例中使用Object.assign()
创建了一个 State 的副本,并且在遇到未知的 Action 时一定要返回旧的 State。按照示例中的写法,当 Action 逐渐增多,整个 Reducer 将会越变越大,这时就需要考虑拆分 Reducer。Reducer 的拆分就必然意味着 Reducer 的合并。使用一个函数来作为主 Reducer,调用若干子 Reducer 来分别处理 State 中的一部分数据,然后再将这些数据合并为一个大的单一对象,这就是开发 Redux 应用最基础的模式。在这种模式下,主 Reducer 不需要设置初始化时完整的 State,各个子 Reducer 会返回各自的初始默认值。
在 ES7 标准中,可以使用对象展开运算符来替代Object.assign()
。上例中的Object.assign()
可以改写为return { ...state, content: action.payload.content }
。
将以上示例改为拆分的写法如下。
function filter(state = initialState, action) {
switch (action.type) {
case SET_FILTER:
return Object.assign({}, state, { filter: action.payload.filter });
default:
return state;
}
}
function add(state = [], action) {
switch (action.type) {
case ADD_CONTENT:
return Object.assign({}, state, { content: action.payload.content });
default:
return state;
}
}
// 主Reducer
function contentApp(state = {}, action) {
return {
filter: filter(state.filter, action),
add: add(state.content, action)
};
}
在这种模式下,随着应用规模的膨胀,拆分后的 Reducer 只需要放置在不同的文件中,保持其独立性即可。针对主 Reducer,Redux 提供了combineReducers()
工具来完成示例中主 Reducer 的事情。所以上例中主 Reducer 可以改写为以下形式。
import { combineReducers } from 'redux';
const contentApp = combineReducers({
filter,
content: add
});
或者还可以为其中不同的 Reducer 设置不同的键值,以定义其要更改的 State。