Open soraping opened 5 years ago
reducer 指定了状态的变化如何响应 actions 并发送到 store 的,记住 actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state
// reducer 样本代码 function todo(state = initialState, action) { switch (action.type) { case SET_VISIBILITY_FILTER: return Object.assign({}, state, { visibilityFilter: action.filter }); default: return state; } }
combineReducers() 是 redux 提供的工具方法,用来合并多个 reducer。
combineReducers()
redux
reducer
import { combineReducers } from "redux"; export default combineReducers({ visibilityFilter, todos });
在 createStore 方法中的第一参数就是这个模块。
createStore
combineReducers 方法接收的参数就是样板代码中的一个一个的 reducer,代码解析:
combineReducers
import ActionTypes from "./utils/actionTypes"; import warning from "./utils/warning"; import isPlainObject from "./utils/isPlainObject"; /** * 经过reducer,返回值不能为undefined * @param {*} key state树中对应的reducer键值 * @param {*} action */ 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.` ); } /** * * @param {state} inputState 当前state树 * @param {*} reducers * @param {*} action * @param {*} unexpectedKeyCache */ 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"; 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." ); } // state是不是纯对象 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('", "')}"` ); } // 过滤出state树中无法识别的,无定义reducer的key const unexpectedKeys = Object.keys(inputState).filter( // 判定条件:reducers中不存在 这个 key 且 unexpectedKeyCache变量中也不存在 key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key] ); unexpectedKeys.forEach(key => { // 此处要注意下,这个地方是内部函数的一个副作用,在此处赋值,会对函数外的引用变量发生改变 // 对,又是闭包 unexpectedKeyCache[key] = true; }); if (action && action.type === ActionTypes.REPLACE) return; 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是否是合法的reducer,即返回的state是不是undefined,如果是undefined,则是非法reducer * * 1. 在初始化阶段,reducer 传入的 state 值是 undefined,此时,需要返回初始state,且初始state不能为undefined * 2. 当传入不认识的 actionType 时, reducer(state, {type}) 返回的不能是undefined * 3. redux/ 这个 namespace 下的action 不应该做处理,直接返回 currentState 就行 (谁运气这么差会去用这种actionType...) */ function assertReducerShape(reducers) { Object.keys(reducers).forEach(key => { const reducer = reducers[key]; const initialState = reducer(undefined, { type: ActionTypes.INIT }); 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.` ); } if ( typeof reducer(undefined, { type: ActionTypes.PROBE_UNKNOWN_ACTION() }) === "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.` ); } }); } /** * 合并reducer * 接收参数: * { * todo: (state, action) => { * ... * return state; * }, * ... * } * @param {*} reducers * * 返回值是一个纯函数,接收当前 state树和action,这个纯函数返回更新后的 state 树 * @return (state, action) => state */ export default function combineReducers(reducers) { const reducerKeys = Object.keys(reducers); const finalReducers = {}; for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i]; if (process.env.NODE_ENV !== "production") { if (typeof reducers[key] === "undefined") { warning(`No reducer provided for key "${key}"`); } } // 把非function的reducer过滤掉,存入变量finalReducers if (typeof reducers[key] === "function") { finalReducers[key] = reducers[key]; } } const finalReducerKeys = Object.keys(finalReducers); // 定义变量存储 state树中的无效key(reducer中不存在) let unexpectedKeyCache; if (process.env.NODE_ENV !== "production") { unexpectedKeyCache = {}; } let shapeAssertionError; try { // 校验reducer是否合法 assertReducerShape(finalReducers); } catch (e) { shapeAssertionError = e; } /** * 最终返回的函数,接收两个参数,在createStore方法中会调用,state为当前树,action为传递的操作 * @return state 经过 action 改变后的 state 树 */ return function combination(state = {}, action) { // 抛出不合法的 reducer 校验 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 = {}; for (let i = 0; i < finalReducerKeys.length; i++) { // state树中的各个reducer对应的值 const key = finalReducerKeys[i]; // 单个reducer const reducer = finalReducers[key]; // 此处是state树中对应的状态值,其key,就是reducers传入combineRefucers所对应各个reducer的key值 const previousStateForKey = state[key]; // 经过reducer,返回改变后的模块state值 const nextStateForKey = reducer(previousStateForKey, action); // reducer的返回值不能为undefined if (typeof nextStateForKey === "undefined") { const errorMessage = getUndefinedStateErrorMessage(key, action); throw new Error(errorMessage); } // 找到对应的key值,把改变后的模块state重新赋值 nextState[key] = nextStateForKey; hasChanged = hasChanged || nextStateForKey !== previousStateForKey; } return hasChanged ? nextState : state; }; }
combineReducers()
是redux
提供的工具方法,用来合并多个reducer
。combineReducers
在
createStore
方法中的第一参数就是这个模块。combineReducers
方法接收的参数就是样板代码中的一个一个的reducer
,代码解析: