最近在我的项目中应用 redux 时遇到一个问题:应用多个 reducer 治理状态(如 ruducerA,reducerB),当通过 action 更新数据时,以后的 reducerA 数据更新胜利,但另一个 reducerB 数据被初始化。
这个行为让我十分蛊惑,排查了很久,一度找不到下手点。代码如下:
APP.js
const rootReducer = combineReducers({
RudecerA,
RudecerB,
});
const store = createStore(rootReducer);
export default function App(props) {
return (<Provider store={store}>
<Router {...props} />
</Provider>
);
}
reducerB.js
export function ReducerB(
state = initState,
action,
) {switch (action.type) {
case UPDATE_RECORD_DATA:
return {...state, recordData: action.payload};
case DELETE_RECORD_DATA:
return {...state, recordData: initRecordData};
default:
return initState;
}
}
解决办法
起初在官网文档看到对于 combineReducer 的介绍及留神点:
所有未匹配到的 action,必须把它接管到的第一个参数也就是那个 state 一成不变返回。
看到这里忽然如同找到了心愿,因为我在 reducerB 中默认返回的是 initState。 于是我试着将 initState 改为 state,问题居然真的解决了!
然而为什么 combineReducer 要求默认返回 state 呢?
诘问溯源
首先咱们应用 combineRuducer 的目标是通过切片形式来治理 state,它最终会合并所有的 state 并返回。
export function combineReducers<S>(reducers: ReducersMapObject<S, any>): Reducer<CombinedState<S>>
通过查看源码,发现 combineRudecer 次要做了以下几件事:
- 过滤不非法的 reducer
- 遍历所有非法的 reducer,并失去新的 state
- 判断 state 是否更新,如果更新就返回新值,否则反之。
- 合并所有 state,并返回。
总结
因为在平时开发中应用 switch case 时,都会给 default 赋值为 initState,避免程序出错。
而在 combineReducer 中每当发动 action 时,以后 reducer 会走到 case 中,返回冀望的后果,但另外的 reducer 没有命中条件,返回的就是初始值。
有时候因为惯性思维而写下一些难以排查的 bug,十分节约精力,在应用这些库时,应该多了解其中原理,让本人少走弯路。