最近在学习redux,对照着官网的示例学习,总有些不明白的地方,所以研究了一下redux源码,主要针对combineReducers、createStore进行分析
具体执行函数请参照test.js,可将该文中的所有内容直接拷贝到Babel中,通过打印出的内容了解执行逻辑。
redux的工作原理:
1. actions: action 内必须使用一个字符串类型的 type 字段来表示将要执行的动作,一般情况下,我们通过Action创建函数来生成action,如function addTodo = (text) => ({type: ADD_TODO,text});
2. reducers: 指明应用如何更新state。reducer就是一个纯函数,接收旧的state和action,返回新的state。通过combinReducers({reducer1,reducer2})将所有的reducer进行合并,返回一个combination(state,action)方法
3. store:通过createStore(reducer)创建store,该store对state进行了初始化,store有以下职责:
维持应用的state;
提供getState()方法获取state;
提供dispatch(action)方法更新state;
通过subscribe(listener)注册监听器;
通过subscribe(listener)返回的函数注销监听器。
1. reducers
每个传入combineReducers的 reducer都需要满足以下规则:
1. 所有未匹配到的action,必须把它接收到的第一个参数也就是那个state原封不动返回。
2. 永远不能返回undefined。当过早return时非常容易犯这个错误,为了避免错误扩散,遇到这种情况时 combineReducers会抛出异常。
3. 如果传入的state是undefined, 一定要返回对应reducer的初始state。根据上一条规则,初始state禁止使用undefined。使用ES6的默认参数值语法来设置初始state很容易,但你也可以手动检查第一个参数是否为undefined
在reducer里不能做如下操作:
1. 修改传入参数
2. 执行有副作用的操作,如API请求和路由跳转
3. 调用非纯函数,如Date.now()或Math.random()
//reducer1 const selectedReddit = (state = 'frontend',action) => { switch(action.type){ case 'SELECT_REDDIT': return action.reddit default return state } } //reducer2 const postsByReddit = (state = { },action) => { switch (action.type) { case INVALIDATE_REDDIT: case RECEIVE_POSTS: case REQUEST_POSTS: return { ...state,//doSomething } default: return state } } 通过combineReducers可以将多个reducer合并到一起
2. combineReducers
function combineReducers(reducers){ var reducerKeys = Object.keys{reducers} var finalReducers = {} //将 key对应的对象不是function的reducer过滤掉 for(var i=0; i < reducerKeys.length; i++){ var key = reducerKeys[i] if(NODE_ENV !== 'production'){ if(typeof reducers[key] === 'undefined'){ warning( `No reducer provided for key "${key}"` ) } } if(typeof reducers[key] === 'function'){ finalReducers[key] = reducers[key] } } //typeof reducer === 'function' var finalReducerKeys = Object.keys(finalReducers) //combineReducers({})执行后,返回combination方法,执行该方法,返回一个合成state对象,state对象的结构由传入的多个reducer的key决定 // state对象的结构: {reducerName1: state1,reducerName2: state2} return function combination(state = {},action){ if (sanityError){ throw sanityError } if(NODE_ENV !== 'production'){ var warningMessage = getUnexpectedStateShapeWarningMessage(state,finalReducers,action,unexpectedKeyCache) if (warningMessage){ warning(warningMessage) } } var hasChanged = false var nextState = {} for(var i = 0; i < finalReducerKeys.length; i++){ var key = finalReducerKeys[i] var reducer = finalReducers[key] var prevIoUsStateForKey = state[key] var nextStateForKey = reducer(prevIoUsStateForKey,action) if(typeof nextStateForKey === 'undefined'){ var errorMessage = getUndefinedStateErrorMessage(key,action) throw new Error(errorMessage) } nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== prevIoUsStateForKey } return hasChanged ? nextState : state } }
3. createStore
通过dispatch(action)执行reducer,通过switch判断该执行哪种操作。 通过getState()获取应用当前state function createStore(reducer,preloadedState,enhancer){ if(typeof preloadedState === 'function' && typeof enhancer === 'undefined'){ enhancer = preloadedState preloadedState = undefined } var currentReducer = reducer var currentState = preloadedState var currentListeners = [] var nextListeners = currentListeners var isDispatching = false function getState(){ return currentState } function dispatch(action){ try{ isDispatching = true console.log(` >>>>>>>>>>>>执行combination start<<<<<<<<<<`); currentState = currentReducer(currentState,action) console.log(` >>>>>>>>>>>>执行combination end,当前state: '${JSON.stringify(currentState)}' <<<<<<<<<<<<`); }finally{ isDispatching = false } var listeners = currentListeners = nextListeners for(var i=0; i < listeners.length; i++){ var listener = listeners[i] listener() } return action } console.log(` >>>>>>>>>>>>>>>>>>>>>初始化state,执行dispatch,start<<<<<<<<<<<<<<<<<<`); dispatch({ type: ActionTypes.INIT }) console.log(` >>>>>>>>>>>>>>>>>>>>>初始化state,执行dispatch,end<<<<<<<<<<<<<<<<<<`); return { dispatch,getState } }