前言
在阅读源码的过程中我也翻阅了大量的资料。一般是先去看别人的源码分析完完整镇过的读一遍之后,看完了之后自己再把官网的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然后进行合并。