zoukankan      html  css  js  c++  java
  • [Redux] redux之combineReducers

    combineReducers

    combineReducer 是将众多的 reducer 合成通过键值映射的对象,并且返回一个 combination 函数传入到 createStore 中
    合并后的 combination 能够调用个子 reducer,并且对 state 状态进行更新

    源码:

    import { ActionTypes } from "./createStore";
    import isPlainObject from "lodash/isPlainObject";
    import warning from "./utils/warning";
    
    //总体上就是根据key和action生成xxx在xxx中出现错误,你需要xxx的错误信息
    function getUndefinedStateErrorMessage(key, action) {
      const actionType = action && action.type;
      const actionDescription = (actionType && `action "${String(actionType)}"`) || "an action";
    
      return `Given ${actionDescription}, reducer "${key}" returned undefined. ` + `To ignore an action, you must explicitly return the previous state. ` + `If you want this reducer to hold no value, you can return null instead of undefined.`;
    }
    
    function getUnexpectedStateShapeWarningMessage(inputState, reducers, action, unexpectedKeyCache) {
      const reducerKeys = Object.keys(reducers);
      const argumentName = action && action.type === ActionTypes.INIT ? "preloadedState argument passed to createStore" : "previous state received by the reducer";
    
      //reducer是一个空对象,没有键值
      if (reducerKeys.length === 0) {
        return "Store does not have a valid reducer. Make sure the argument passed " + "to combineReducers is an object whose values are reducers.";
      }
    
      //检查 value 是否是普通对象。 也就是说该对象由 Object 构造函数创建,或者 [[Prototype]] 为 null
      //https://www.html.cn/doc/lodash/#_isplainobjectvalue
      if (!isPlainObject(inputState)) {
        return `The ${argumentName} has unexpected type of "` + {}.toString.call(inputState).match(/s([a-z|A-Z]+)/)[1] + `". Expected argument to be an object with the following ` + `keys: "${reducerKeys.join('", "')}"`;
      }
    
      //如果一些key在state中存在,而在reducer中不存在,则添加至unexpectedKeyCache中并输出警告信息
      const unexpectedKeys = Object.keys(inputState).filter(key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key]);
    
      unexpectedKeys.forEach(key => {
        unexpectedKeyCache[key] = true;
      });
    
      if (unexpectedKeys.length > 0) {
        return `Unexpected ${unexpectedKeys.length > 1 ? "keys" : "key"} ` + `"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` + `Expected to find one of the known reducer keys instead: ` + `"${reducerKeys.join('", "')}". Unexpected keys will be ignored.`;
      }
    }
    
    //判断reducer是否符合redux的标准
    function assertReducerShape(reducers) {
      Object.keys(reducers).forEach(key => {
        const reducer = reducers[key];
        // 通过ActionTypes.INIT来(oldState,action) => return newState测试reducer是否正确
        const initialState = reducer(undefined, { type: ActionTypes.INIT });
    
        //如果返回是undefined说明reducer内部出错,不符合使用标准
        if (typeof initialState === "undefined") {
          throw new Error(`Reducer "${key}" returned undefined during initialization. ` + `If the state passed to the reducer is undefined, you must ` + `explicitly return the initial state. The initial state may ` + `not be undefined. If you don't want to set a value for this reducer, ` + `you can use null instead of undefined.`);
        }
    
        const type =
          "@@redux/PROBE_UNKNOWN_ACTION_" +
          Math.random()
            .toString(36)
            .substring(7)
            .split("")
            .join(".");
    
        // 通过随机生成type来(oldState,action) => return newState测试reducer是否正确,如果返回是undefined说明reducer内部出错,不符合使用标准
        if (typeof reducer(undefined, { type }) === "undefined") {
          throw new Error(`Reducer "${key}" returned undefined when probed with a random type. ` + `Don't try to handle ${ActionTypes.INIT} or other actions in "redux/*" ` + `namespace. They are considered private. Instead, you must return the ` + `current state for any unknown actions, unless it is undefined, ` + `in which case you must return the initial state, regardless of the ` + `action type. The initial state may not be undefined, but can be null.`);
        }
      });
    }
    
    /**
     * Turns an object whose values are different reducer functions, into a single
     * reducer function. It will call every child reducer, and gather their results
     * into a single state object, whose keys correspond to the keys of the passed
     * reducer functions.
     *
     * @param {Object} reducers An object whose values correspond to different
     * reducer functions that need to be combined into one. One handy way to obtain
     * it is to use ES6 `import * as reducers` syntax. The reducers may never return
     * undefined for any action. Instead, they should return their initial state
     * if the state passed to them was undefined, and the current state for any
     * unrecognized action.
     *
     * @returns {Function} A reducer function that invokes every reducer inside the
     * passed object, and builds a state object with the same shape.
     */
    
    //这个函数可以组合一组 reducers(对象) ,然后返回一个新的 reducer 函数给 createStore 使用
    export default function combineReducers(reducers) {
      const reducerKeys = Object.keys(reducers);
      //用来存放过滤后的值
      const finalReducers = {};
      for (let i = 0; i < reducerKeys.length; i++) {
        const key = reducerKeys[i];
    
        //开发环境下如果reducer是undefined,报警
        if (process.env.NODE_ENV !== "production") {
          if (typeof reducers[key] === "undefined") {
            warning(`No reducer provided for key "${key}"`);
          }
        }
    
        //如果reducer不是一个函数,就过滤掉
        if (typeof reducers[key] === "function") {
          finalReducers[key] = reducers[key];
        }
      }
      const finalReducerKeys = Object.keys(finalReducers);
    
      let unexpectedKeyCache;
      if (process.env.NODE_ENV !== "production") {
        unexpectedKeyCache = {};
      }
    
      //第二次过滤,将finalReducer中不符合redux的标准的reducer去掉
      let shapeAssertionError;
      try {
        assertReducerShape(finalReducers);
      } catch (e) {
        shapeAssertionError = e;
      }
    
      //整个combineReducer就是返回一个 combination 函数,该函数将传入createStore 中
      return function combination(state = {}, action) {
        //当执行第二次过滤是出现错误,抛出
        if (shapeAssertionError) {
          throw shapeAssertionError;
        }
    
        if (process.env.NODE_ENV !== "production") {
          //开发环境下,报警
          const warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache);
          if (warningMessage) {
            warning(warningMessage);
          }
        }
    
        let hasChanged = false;
        const nextState = {};
        //遍历所有的reducer来执行(oldState,action)=>newState,根据hanChanged来判断是返回新state还是旧state (性能)
        for (let i = 0; i < finalReducerKeys.length; i++) {
          const key = finalReducerKeys[i]; //当前key值
          const reducer = finalReducers[key]; //当前key值对应的函数
          const previousStateForKey = state[key]; //当前reducer的旧状态
          const nextStateForKey = reducer(previousStateForKey, action); //为reducer计算出新state
          if (typeof nextStateForKey === "undefined") {
            // 如果计算出的新state是undefiend,通过getUndefinedStateErrorMessage拼接个报错信息,抛出去
            const errorMessage = getUndefinedStateErrorMessage(key, action);
            throw new Error(errorMessage);
          }
          //将所有新计算的state组成新state树
          nextState[key] = nextStateForKey;
          //判断新state是否等于旧state,如果不同就将hasChanged设为false,代表整个状态都改变了(只要有一个不同)
          hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
        }
        // 根据hasChanged的值来返回就state还是新state
        return hasChanged ? nextState : state;
      };
    }
    
    

    Redux 之 combineReducers(reducers)详解

    Redux 源码浅析系列(二):combineReducer

    redux 的 isPlainObject 源码

  • 相关阅读:
    函数waitpid和WTERMSIG说明(转)
    WIFEXITED WEXITSTATUS WIFSIGNALED(转)
    有关于malloc申请内存和free内存释放
    Using 1-Wire device with Intel Galileo
    Intel Galileo驱动单总线设备(DHT11DHT22)(转)
    360度舵机和180度舵机控制方法小结(转)
    warning: the `gets' function is dangerous and should not be used.(转)
    C语言fgetpos()函数:获得当前文件的读写指针(转)
    关于arduino清空串口缓存(转)
    修改Arduino串口缓冲区大小(转)
  • 原文地址:https://www.cnblogs.com/mybilibili/p/10322121.html
Copyright © 2011-2022 走看看