redux——combineReducers原理

it2023-07-13  68

import ActionTypes from './utils/actionTypes' import warning from './utils/warning' import isPlainObject from './utils/isPlainObject' // reducer的返回值为undefined时的报错信息 function getUndefinedStateErrorMessage(key, action) { // 判断action有没有type 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 {*} inputState 当前仓库的状态 * @param {*} reducers reducers * @param {*} action 当前分发的action * @param {*} unexpectedKeyCache */ function getUnexpectedStateShapeWarningMessage( inputState, reducers, action, unexpectedKeyCache ) { // reducers的对象属性名 const reducerKeys = Object.keys(reducers) // 判断当前的action的type是否和INIT一样 const argumentName = action && action.type === ActionTypes.INIT ? 'preloadedState argument passed to createStore' : 'previous state received by the reducer' // reducers必须包含至少有一个有效的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.' ) } // 传入的仓库默认值要是一个平面对象 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('", "')}"` ) } // 判断当前的仓库状态是否有没有和reducers匹配的值 const unexpectedKeys = Object.keys(inputState).filter( key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key] ) unexpectedKeys.forEach(key => { unexpectedKeyCache[key] = true }) // 判断action的type是否和REPLACE相同 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的返回值是不是undefined function assertReducerShape(reducers) { Object.keys(reducers).forEach(key => { const reducer = reducers[key] // 调用每一个reducer 传入undefined 判断返回值是不是undefined 如果是undefined就报错 const initialState = reducer(undefined, { type: ActionTypes.INIT }) // 返回值为undefined报错 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.` ) } // 再调用一次reducer // 这次的action的type值为随机数 防止用户做了在reducer中做了判断 让上面的type值为INIT的action失效 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的对象 export default function combineReducers(reducers) { // 获取每个reducer的参数名 const reducerKeys = Object.keys(reducers) // 最后返回的包含reducer的对象 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}"`) } } // 如果该对象值为一个函数 把该对象添加到finalReducers中 if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } const finalReducerKeys = Object.keys(finalReducers) // This is used to make sure we don't warn about the same // keys multiple times. let unexpectedKeyCache if (process.env.NODE_ENV !== 'production') { unexpectedKeyCache = {} } let shapeAssertionError try { // 验证reducer的返回值是不是undefined assertReducerShape(finalReducers) } catch (e) { shapeAssertionError = e } return function combination(state = {}, action) { // 如果有reducer返回值为undefined 直接报错 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++) { // reducers对象属性名 const key = finalReducerKeys[i] // reducer const reducer = finalReducers[key] // 获取当前对应reducer的仓库状态state const previousStateForKey = state[key] // 调用reducer得到新的仓库状态state const nextStateForKey = reducer(previousStateForKey, action) // 如果得到的仓库状态state为undefined报错 if (typeof nextStateForKey === 'undefined') { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } // 保存更新后的仓库状态 nextState[key] = nextStateForKey // 通过比较前后两次仓库状态state判断当前的仓库状态有没有改变 hasChanged = hasChanged || nextStateForKey !== previousStateForKey } // 通过仓库状态的对象和reducers的对象长度是否一致 来判断是否修改过 hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length // 判断当前的仓库状态有没有改变 返回新的仓库状态state return hasChanged ? nextState : state } }
最新回复(0)