https://github.com/reduxjs/redux 版本 4.0.0
先了解一下redux是怎么用的,此处摘抄自阮一峰老师的《Redux 入门教程》
// Web 应用是一个状态机,视图与状态是一一对应的 // 所有的状态,保存在一个对象里面 // store 是保存数据的地方 // 创建 store import { createStore } from 'redux' const store = createStore(fn) // state 是某一时刻 store 的快照,一个 state 对应一个 view // 可通过 getState() 获取 const state = store.getState() // Action 是一个对象 用来表示 view 发出的改变 state 的通知 // type 是必须的 其他属性可以自由设置 const action = { type: 'ADD_TODO', payload: 'Learn Redux' } // 同一种类型的 action 可以写一个函数生成 const ADD_TODO = '添加 TODO' // 生成 action 的函数: Action Creator function addTodo(text) { return { type: ADD_TODO, text } } const action = addTodo('Learn Redux') // store.dispatch()是 View 发出 Action 的唯一方法。 store.dispatch(action) // reducer 是 store 接收 state 返回新的 state 的过程 const defaultState = 0 // reducer 接收 action 返回新的 state const reducer = (state = defaultState, action) => { switch(action.type) { case: 'ADD': return state + action.payload default: return state } } const state = reducer(1, { type: 'ADD', payload: 2 }) // 创建 store 时传入 reducer 当调用 store.dispatch 时将自动调用 reducer const store = createStore(reducer) /* reducer 是一个纯函数,纯函数要求: - 不得改写参数 - 不能调用系统 I/O 的API - 不能调用Date.now()或者Math.random()等不纯的方法,因为每次会得到不一样的结果 */ // store.subscribe方法设置监听函数,一旦 State 发生变化,就自动执行这个函数 // 返回解除监听函数 let unsubscribe = store.subsribe(() => { console.log(store.getState) }) unsubscribe() // 解除监听 /* store 提供的三个方法 - store.getState() - store.dispatch() - store.subscribe() */ // createStore方法还可以接受第二个参数,表示 State 的最初状态。这通常是服务器给出的。 // !这个初始值会覆盖 Reducer 函数默认的初始值 let store = createStore(todoApp, STATE_FROM_SERVER) // createStore 的简单实现 const createStore = (reducer) => { let state let listeners = [] const getState = () => state const dispatch = action => { state = reducer(state, action) listeners.forEach(listener => listener()) } const subscribe = listener => { listeners.push(listener) return () => { listeners = listeners.filter(l => l !== listener) } } dispatch({}) return { getState, dispatch, subscribe } } // 可以通过 combineReducers 来将多个 Reducer 合为一个 import { combineReducers } from 'redux' const chatReducer = combineReducers({ chatLog, statusMessage, userName }) // combineReducer 的简单实现 const combineReducers = reducers => { return (state = {}, action) => Object.keys(reducers).reduce( (nextState, key) => { nextState[key] = reducers[key](state[key], action) return nextState }, {} ) }
工作流程
Redux Flow dispatch(action) (previousState, action) Action Creators ======> Store ======> Reducers ^ || <====== \_ || (newState) \_ (state) || \_ || (view opt)\_ \/ \--- React Comonents
OK 可以开始看源码了~ 网上Redux源码分析的博客真的非常多.. 不过当你知道他的源码究竟有多短 就能理解了hhh
combineReducers.js
代码一共179行 多是错误处理 我先将错误处理全部删掉 便只剩28行.....
思路就是创建一个对象 将 Reducer 全部放进去
当Action传进来的时候 就让每一个Reducer去处理这个action
每个Reducer都有一个对应的key 只处理state中对应字段 state[key] 没有Reducer对应的字段会被忽略
截取出核心代码 + 用法、感觉并不需要注释、逻辑都很直接
function combineReducers(reducers) { const reducerKeys = Object.keys(reducers) const finalReducers = {} for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i] if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } const finalReducerKeys = Object.keys(finalReducers) return function combination(state = {}, action) { let hasChanged = false const nextState = {} for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] const reducer = finalReducers[key] const previousStateForKey = state[key] const nextStateForKey = reducer(previousStateForKey, action) nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } // 如果state每一个key都没有被修改 就直接返回原state return hasChanged ? nextState : state } } /***************** 下面是简单的用法实例 *****************/ function todos(state = [], action) { switch (action.type) { case 'ADD_TODO': return state.concat(action.text) default: return state } } function counter(state = 0, action) { switch (action.type) { case 'INCREMENT': return state + 1 case 'DECREMENT': return state - 1 default: return state } } let reducer = combineReducers({ list: todos, number: counter }) let state = { list: [], number: 0, otherKey: 'no reducer match will be ignore' } console.log(state) // { list: [], number: 0, otherKey: 'no reducer match will be ignore' } state = reducer(state, { type: 'ADD_TODO', text: 'study' }) console.log(state) // { list: [ 'study' ], number: 0 } state = reducer(state, { type: 'ADD_TODO', text: 'sleep' }) console.log(state) // { list: [ 'study', 'sleep' ], number: 0 } state = reducer(state, { type: 'INCREMENT' }) console.log(state) // { list: [ 'study', 'sleep' ], number: 1 }
combineReducers.js 源码
import ActionTypes from './utils/actionTypes' import warning from './utils/warning' import isPlainObject from './utils/isPlainObject' 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' 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)) { // 希望 inputState 是一个简单对象:通过 new Object() 、 {} 创建 (Object.create(null) 这里好像是不合法的 // [object Array] 中提取 'Array' // Object.prototype.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] 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('", "')}"` ) } // 检查所有Reducer都没有处理到的key ( 此处实在不解 unexpectedKeyCache 到底何用= = const unexpectedKeys = Object.keys(inputState).filter( key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key] ) unexpectedKeys.forEach(key => { unexpectedKeyCache[key] = true }) // 替换 store 的 Reducer 时会调用 dispatch({ type: ActionTypes.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.` ) } } function assertReducerShape(reducers) { Object.keys(reducers).forEach(key => { const reducer = reducers[key] const initialState = reducer(undefined, { type: ActionTypes.INIT }) // Reducer"$ {key}"在初始化时返回undefined。如果传递给reducer的状态未定义,你必须明确返回初始状态。 // 初始状态可以是不可定义。如果你不想为这个reducer设置一个值,你可以使用null而不是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.` ) } if ( typeof reducer(undefined, { type: ActionTypes.PROBE_UNKNOWN_ACTION() }) === 'undefined' ) { // 当使用随机类型探测Reducer${key}时返回undefined。 // 不要试图处理${ActionTypes.INIT}或者其他在"redux/*"命名空间的动作。它们被认为是私有的。 // 相反,当你遇到任何未知动作时,你必须返回当前的state,除非当前state是undefined, // 那样你要返回初始状态,而不管动作类型。初始状态不可以是undefined,但可以为null 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. */ 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}"`) } } if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } const finalReducerKeys = Object.keys(finalReducers) let unexpectedKeyCache if (process.env.NODE_ENV !== 'production') { unexpectedKeyCache = {} } let shapeAssertionError try { // 判断每个reducer都有初始值和对于未知action返回原state assertReducerShape(finalReducers) } catch (e) { shapeAssertionError = e } 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 = {} for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] const reducer = finalReducers[key] const previousStateForKey = state[key] const nextStateForKey = reducer(previousStateForKey, action) if (typeof nextStateForKey === 'undefined') { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } // 如果state每一个key都没有被修改 就直接返回原state return hasChanged ? nextState : state } }