1. 程式人生 > >Redux原理探索

Redux原理探索

finally function CI www. obs turn segment blog n)

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);
}
... const mapDispatchToProps
= (dispatch) => { return bindActionCreators({ changeCurrentMonday: actions.changeCurrentMonday, changeCurrentMonth: actions.changeCurrentMonth, changeCurrentSchedule: actions.changeCurrentSchedule }, dispatch); } export default connect(null, mapDispatchToProps)(Schedule);
applyMiddleware,先看看使用方法
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原理探索