Redux記錄:Store是如何自動調用reducers來處理action的
作爲一個後端程序員,經常也要寫一點前端、維護一下前端。因此一直在與前端打交道,但是一直沒有理解當用戶操作view通過dispatch發出 action之後,我們定義的一系列的reducer是如何來自動執行處理的。
先說結論:當用戶操作view之後發出一個action,store會遍歷所有的reducers來依次處理這個action來改變state。
今天瀏覽自己所在公司的官方博客,發現了這篇文章:Redux從設計到源碼,仔細拜讀了一下,收穫很大,也解決了自己一直以來的困惑。
藉此機會,自己也梳理一下。
在前端代碼,自己經常看到類似如下的代碼:
let store = createStore(reducers, undefined, compose(
applyMiddleware(
thunk,
fetchMiddleware
),
window.devToolsExtension ? window.devToolsExtension() : f => f
));
其中,reducers如下:
let reducers = combineReducers({AReducer, BReducer,CReducer});//AReducer等是我們自己定義的reducer
那麼,當一個type=“A”的action產生後,是如何去這些{AReducer, BReducer,CReducer}來匹配查找然後進行處理的呢?自己當時的猜想是遍歷所有的,看了源碼之後原來真的是這樣。
combineReducers
先看combineReducers的源碼裏面做了些什麼,源碼如下:
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 {
assertReducerShape(finalReducers)
} catch (e) {
shapeAssertionError = e
}
//返回的是如下這個函數,用於處理action
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
}
return hasChanged ? nextState : state
}
}
上面的代碼比較長,只需要注意兩點即可:
1、將一系列的reducer以(reducerKey,reducer)存儲在finalReducers中。
2、返回的函數將在store中自動調用,來處理action。至於如何處理的,下面分析完createStore之後將會分析。
createStore
下面是createStore方法的部分源碼,由於store.dispatch(action)是用來分發action,這是修改state的唯一方式,基於此我們這裏只關注dispatch方法,因此只對其進行了保留,如果想對其他方法有了解,可以參考博文:Redux從設計到源碼,
export default function createStore(reducer, preloadedState, enhancer) {
//省略類型檢查
let currentReducer = reducer
let currentState = preloadedState
let currentListeners = []
let nextListeners = currentListeners
let isDispatching = false
//省略其他方法,只保留了dispatch方法
function dispatch(action) {
//省略了類型檢查
try {
isDispatching = true
currentState = currentReducer(currentState, action) //分析
} finally {
isDispatching = false
}
const listeners = currentListeners = nextListeners
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
}
store.dispatch()方法總結:
1、調用Reducer,傳參(currentState,action)。
2、按順序執行listener。
3、返回action。
下面分析第一點:調用Reducer,傳參(currentState,action),即如下這行代碼
currentState = currentReducer(currentState, action)
前面我們說過,currentReducer所指的就是由這行代碼let reducers = combineReducers({AReducer, BReducer,CReducer});
所產生的reducers,這個reducers是如下這個函數:
function combination(state = {}, action) {
//省略了部分檢查代碼
let hasChanged = false
const nextState = {}
//對所有的reducers進行遍歷來處理action。
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
//利用這個reducer來處理action
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
//保存state並判斷狀態是否改變了。
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}
上面比較簡單哈,看完這裏的源碼是不是就理解了,當用戶操作view產生一個action之後,store是如何自動調用reducers來處理action的哈。