Redux原理探索
Redux
redux的index.js暴露以下幾個接口
export {
createStore,
combineReducers,
bindActionCreators,
applyMiddleware,
compose,
__DO_NOT_USE__ActionTypes
}
先看createStore方法,下面是使用方法
const store = createStore(rootReducer)
下面為createStore的實現過程及返回值
//初始化時設置一個默認的action dispatch({ type: ActionTypes.INIT })return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable }
createStore返回了一些方法給store
- dispatch是唯一一個改變state的方法,用來更新state數據並觸發監聽
- subscribe訂閱監聽,返回一個方法解除監聽
- getState返回state數據
- replaceReducer替換掉當前的reducer並初始化數據
dispatch在執行時先進行驗證保證dispatch的參數action是有效的,然後執行下述代碼
try { isDispatching = true //改變state 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
通過currentState = currentReducer(currentState, action)來改變state,其中currentReducer是初始化store時傳入的參數rootReducer。
看看currentReducer是什麽
export default combineReducers({ todos, visibilityFilter })
從使用方法上看是combineReducers接口的返回值
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) if (typeof nextStateForKey === ‘undefined‘) { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } //將改變後的state存放到全局變量 nextState[key] = nextStateForKey //判斷state是否改變 只要有一個key變了就認為state改變了 hasChanged = hasChanged || nextStateForKey !== previousStateForKey } //根據是否改變選擇返回舊state還是新的state return hasChanged ? nextState : state }
返回的是combineReducers閉包函數,在返回之前先對reducer裏面的方法進行驗證保證方法好用
currentReducer(currentState, action)實際上就是combination(currentState, action),與舊的state進行比較來確定是否更新
bindActionCreators接口是將action被dispatch包裹使用時不再調用dispatch bindActionCreators官方文檔componentdidmount(){ const {changeCurrentMonday,changeCurrentMonth} = this.props; changeCurrentMonday(data); }applyMiddleware,先看看使用方法
... const mapDispatchToProps = (dispatch) => { return bindActionCreators({ changeCurrentMonday: actions.changeCurrentMonday, changeCurrentMonth: actions.changeCurrentMonth, changeCurrentSchedule: actions.changeCurrentSchedule }, dispatch); } export default connect(null, mapDispatchToProps)(Schedule);
const store = createStore(reducer,applyMiddleware(mid1,mid2));
再回頭看看createStore方法,方法一共三個形參:reducer, preloadedState, enhancer
// 第二個參數是function且第三個參數不存在時,將二三參數互換 if (typeof preloadedState === ‘function‘ && typeof enhancer === ‘undefined‘) { enhancer = preloadedState preloadedState = undefined } //確保第三個參數enhancer是一個函數 如果是 用enhancer(createStore)(reducer, preloadedState)作為返回值 if (typeof enhancer !== ‘undefined‘) { if (typeof enhancer !== ‘function‘) { throw new Error(‘Expected the enhancer to be a function.‘) } return enhancer(createStore)(reducer, preloadedState) }
所以上面的createStore執行的實際上是applyMiddleware(mid1,mid2)(createStore)(reducer, preloadedState),applyMiddleware返回一個閉包函數,最終執行的代碼為
function(reducer, preloadedState) { const store = createStore(reducer, preloadedState) let dispatch = () => { throw new Error( `Dispatching while constructing your middleware is not allowed. ` + `Other middleware would not be applied to this dispatch.` ) } const middlewareAPI = { getState: store.getState, dispatch: (reducer, preloadedState) => dispatch(reducer, preloadedState) } const chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } }
applyMiddleware是通過增強dispatch實現的,最終將增強後的dispatch返回,其中middlewareAPI 中的dispatch使用()=>dispatch()是為了保證每一個中間件的dispatch引用獨立。
假如有兩個中間件func1,func2(中間件格式({state,dispatch})=>(next)=>(action)=>{return next(aciton);})
chain的格式為
[//func1
(next)=>(action)=>{next(action);},
//func2
(next)=>(action)=>{next(action);}]
現在看一下compose實現
export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) }compose(...chain)(store.dispatch)實際執行為func1(func2(store.dispatch)) func1的實參為(action)=>{store.dispatch(action);}即func1中間件的next為(action)=>{store.dispatch(action); 最後dispatch的值為(action)=>{next(action);},next為func1的返回值,將這個dispatch返回給store。 每次調用dispatch時就會通過(action)=>{next(action);}一層層執行下去直到遇到(action)=>{store.dispatch(action)更新state操作 下圖即中間件調用流程 在中間件執行過程中不能使用store.dispatch應該用next進行傳遞,否則會出現死循環
redux-thunk原理
function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === ‘function‘) { return action(dispatch, getState, extraArgument); } return next(action); }; } const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk;
thunk異步就是通過不調用next(action)跳出循環實現的。
通過dispatch一個異步function(例func1)攔截更新state,在func1中再dispatch一個action實現異步更新state數據。
參考文章:
https://segmentfault.com/a/1190000004485808
https://github.com/MrErHu/blog/issues/1
https://github.com/ecmadao/Coding-Guide/blob/master/Notes/React/Redux/Redux%E5%85%A5%E5%9D%91%E8%BF%9B%E9%98%B6-%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90.md
Redux原理探索