前言
在阅读源码的过程中我也翻阅了大量的资料。一般是先去看别人的源码分析完完整镇过的读一遍之后,看完了之后自己再把官网的clone下来自己再慢慢的阅读添加注释。希望大家在阅读完有感想之后也把官网源码clone下来不照着任何的资料边写注释边阅读完全部源码,这样子更有利于深刻理解。
在工作中其实会经常使用到 redux 数据状态处理库,在一开始的使用中就一直听到说 reducer 必须是一个纯函数,不能有副作用!state 需要有一个默认值!等等的约束。当然也踩过含有副作用修改了 state 造成视图层不更新的 bug(state 嵌套过深的锅。。。) 一直停留在知其然不知其所以然的层次 想到彻底的掌握以及明白 redux 的原理的办法当然就是直接阅读源码啦 而且 redux 非常简洁才 2kb 而已= = 非常值得一读
最开始我们分析 createStore 函数的源码,接下来还有一个 api 我们会经常使用到就是 combineReducer,用于把多个分模块的子 reducer 生成一个总的 reducer
combineReducers 的基本使用
1 //常用的三个api
2 import { createStore, combineReducers, applyMiddleware } from "redux";
3
4 import { userReducer } from "./user/reducer";
5 import { todoReducer } from "./todo/reducer";
6 //combineReducers用于合并多个子reducer生成一个总的reducer
7 const reducers = combineReducers({
8 userStore: userReducer,
9 todoStore: todoReducer
10 });
明白了 combineReducers 的基本用法之后我们就可以深入源码啦
上源码,为了减小篇幅,我删减了很多没用的代码(包括一些错误边界处理,其实很多错误边界处理都很有意思),如果想看完整的redux代码注释的话可以点击这里。
combineReducers
通过源码 我们可以看到 combineReducers 其实接受要合并的 reducer 对象 返回 combination 函数 其实 combination 还是一个 reducer dispatch(action)的时候 会依次调用子 reducer 计算出子 reducer 的 state 值再而合并成对象。
- combineReducers 一开始会循环所有的子 reducer 筛选出可用的 reducer(state 不能为 underfined 子 reducer 在 redux 内部自定义 action 的时候必须返回默认值 state)并且生成真正可用的 finalReducers
- dispatch(action)的时候 会循环所有的子 reducer 传入 action 依次生成新的子 state 值 之后浅比较之前的 state 和新生成的 state 如果浅比较不相同就把 hasChanged 赋值为 true 证明子 state 改变了自然而然总 state 也改变了
- combination 在返回 state 值时会进行判断 判断当前的 hasChanged 是否为 true 是的话证明 state 发生了变化返回新的 state 不然 state 没有变化返回旧的 state 值
1 export default function combineReducers(reducers) {
2 //获取所有子reducers的key值
3 const reducerKeys = Object.keys(reducers);
4 //筛选后可用的reducers
5 const finalReducers = {};
6 for (let i = 0; i < reducerKeys.length; i++) {
7 const key = reducerKeys[i];
8 /*
9 * 开发环境下下遍历所有子reducers的value值
10 * 如果value为undefined 抛出警告
11 * 即 combineReducers({a:aReducer,b:bReducer}) 中的aReducer 不能为underfined
12 * */
13 if (process.env.NODE_ENV !== "production") {
14 if (typeof reducers[key] === "undefined") {
15 warning(`No reducer provided for key "${key}"`);
16 }
17 }
18
19 //进行筛选 筛选出函数类型的reducer
20 if (typeof reducers[key] === "function") {
21 finalReducers[key] = reducers[key];
22 }
23 }
24 const finalReducerKeys = Object.keys(finalReducers);
25
26 let shapeAssertionError;
27 try {
28 //assertReducerShape是一个错误处理函数判断子reducer在传入一个非预定好的action时 是否会返回默认的state
29 assertReducerShape(finalReducers);
30 } catch (e) {
31 shapeAssertionError = e;
32 }
33
34 return function combination(state = {}, action) {
35 //state是否改变 这里判断state是否改变是通过浅比较的 所以才要求每次返回的state都是一个全新的对象
36 let hasChanged = false;
37 //新的state值 这里的state是根rootReducer的state
38 const nextState = {};
39 for (let i = 0; i < finalReducerKeys.length; i++) {
40 const key = finalReducerKeys[i];
41 //根据key值获取相当应的子reducer
42 const reducer = finalReducers[key];
43 //获取上一次当前key值所对应的state值 下面要进行浅比较
44 const previousStateForKey = state[key];
45 //获取传入action之后新生成的state值
46 const nextStateForKey = reducer(previousStateForKey, action);
47 if (typeof nextStateForKey === "undefined") {
48 const errorMessage = getUndefinedStateErrorMessage(key, action);
49 throw new Error(errorMessage);
50 }
51 //循环执行reducer 把新的值进行存储
52 nextState[key] = nextStateForKey;
53 //浅比较 这里把旧的子reducer state值 与传入action之后生成的state值进行浅比较 判断state是否改变了
54 hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
55 }
56 //根据判断赶回state 只要有一个子reducer hasChanged为true那么就重新返回新的nextState 所以这里揭示了为什么reducer必须是纯函数而且如果state改变了必须返回一个新的对象
57 //如果返回的是依然的state对象(有副作用的push,pop方法)如果state是对象 因为nextStateForKey !== previousStateForKey比较的是引用 那么 hasChanged认为是false没有发生改变 自然而然下面返回的state依然是旧的state
58 return hasChanged ? nextState : state;
59 };
60 }
61
62 function assertReducerShape(reducers) {
63 Object.keys(reducers).forEach(key => {
64 //遍历reducer
65 const reducer = reducers[key];
66 //先依次执行reducer 看是否有默认返回值state 其中ActionTypes.INIT为内部自定义的action 自然而然的执行到default 如果返回undefined 抛出错误 state要有默认值
67 const initialState = reducer(undefined, { type: ActionTypes.INIT });
68
69 if (typeof initialState === "undefined") {
70 throw new Error(
71 `Reducer "${key}" returned undefined during initialization. ` +
72 `If the state passed to the reducer is undefined, you must ` +
73 `explicitly return the initial state. The initial state may ` +
74 `not be undefined. If you don't want to set a value for this reducer, ` +
75 `you can use null instead of undefined.`
76 );
77 }
78 if (
79 typeof reducer(undefined, {
80 type: ActionTypes.PROBE_UNKNOWN_ACTION()
81 }) === "undefined"
82 ) {
83 throw new Error(
84 `Reducer "${key}" returned undefined when probed with a random type. ` +
85 `Don't try to handle ${
86 ActionTypes.INIT
87 } or other actions in "redux/*" ` +
88 `namespace. They are considered private. Instead, you must return the ` +
89 `current state for any unknown actions, unless it is undefined, ` +
90 `in which case you must return the initial state, regardless of the ` +
91 `action type. The initial state may not be undefined, but can be null.`
92 );
93 }
94 });
95 }
总结
combineReducers的返回值实质上就是一个reducer函数,这个返回的reducer函数会把action传入各个子reducer中获取子state然后进行合并。