redux源码浅析(2)

前言

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

posted @ 2018-09-17 14:47  carrot萝卜  阅读(154)  评论(0编辑  收藏  举报