redux 核心更新 flow dispatch action => reducer => store,當你用 redux 運行多個 dispatch 時,每一個 dispatch 都會獨立更新下去的,這代表著你會 update component 多次,如果你的更新資料又彼此關聯,就可能會發生錯誤。

目前執行的專案,資料都是 linked list,若沒有注意好 dispatch 更新執行順序的話,時常會遇到問題。

redux-dispatch

  • 多個 dispatch 更新
    function initINFO (){
    fetch(`API_URL/getCommentList`)
    .then(res => res.json)
    .then(data =>
    // if ADD_USERCOMMENT update component
    // and it depends userInfo data
    // component maybe happen error
    dispatch({type: ADD_USERCOMMENT, payload: data.list})
    if (data.userInfo) {
    dispatch({type: INIT_INFO, payload: data.userInfo})
    }
    )
    .catch(err => showError(err))
    }

處理的做法就是直接封裝整個 dispatch,讓每個 dispatch 都先不要往下執行 update component。

redux batch

react redux v7 有提供 batch,讓我們能直接封裝多個 dispatch,這是依賴 react 的 unstable_batchedUpdate,讓 rerender 這件事情能夠被卡住,react 實現方法大致上就是用 shouldBatchUpdates 變數搭配 fiber schedule 來判斷更新,讓更新這件事變成同步,詳細可直接看下方 react unstable_batchedUpdates source code。

  • batch 使用方法
    import { batch } from 'react-redux'

    function myThunk() {
    return (dispatch, getState) => {
    // should only result in one combined re-render, not two
    batch(() => {
    dispatch(increment())
    dispatch(increment())
    })
    }
    }

react redux github batch Q&A

react unstable_batchedUpdates source code

redux thunk

redux thunk 同樣可以幫助我們處理多個 dispatch,但是與 batch 原理不大相同,redux thunk,是將 dispatch 往後延遲到最後一次執行。

乍聽之下可能覺得這有點魔幻,但我貼上 redux thunk 的介紹你一定會恍然大悟。redux thunk 就是將 dispatch 封裝起來在最後一次真正執行 store.dispatch,所以你就只會觸發一次的 redux store update,進而達到只 rerender 一次。

  • what is thunk

    // calculation of 1 + 2 is immediate
    // x === 3
    let x = 1 + 2;

    // calculation of 1 + 2 is delayed
    // foo can be called later to perform the calculation
    // foo is a thunk!
    let foo = () => 1 + 2;
  • Add thunk on redux middleWare

    import { createStore, applyMiddleware } from 'redux';
    import thunk from 'redux-thunk';
    import reducers from '../redux/reducers';

    const store = createStore(reducers, applyMiddleware(thunk));


    function fetchUser(data) {
    return (dispatch, getState) => {
    dispatch({type: ADD_USERCOMMENT, payload: data.list})
    if (data.userInfo) {
    dispatch({type: INIT_INFO, payload: data.userInfo})
    }
    };
    }

extraArgument 不用理會,這是新功能讓使用者客製化增加 thunk 的參數,action 會是我們傳入的 action creator,當 action creator 是一個 function,就執行 action creator function,如果不是就執行 next 帶入 action creator,正常的執行 dispatch。

redux applymiddleware

  • thunk source code
    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;

redux thunk

  • 舊版 applyMiddleWare (github 只剩 typescript…)
    // middlewares argument is pass thunk
    export default function (...middlewares) {
    return (createStore) => (reducers, initialState, enhancer) => {
    const store = createStore(reducers, initialState, enhancer)
    const dispatch = store.dispatch
    const chain = []
    const middleWareAPI = {
    getState: store.getState,
    dispatch: action => dispatch(action)
    }

    chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch) // store.dispatch or dispatch both work
    // compose will do following thing:
    /*
    * a, b, c ==> a(b(c())), indeed, it is just a reduce and store.dispatch will be an initial value
    */
    return {
    ...store,
    dispatch
    }
    }
    }

redux thunk code 很優美,完美的示範如何使用 redux 的 middleWare,邏輯清楚又不複雜。我一定不會說這篇文章是為了分享 redux thunk。

改天再來研究、分享更優美的 redux。

這兩個方法 batchredux thunk 都是目前專案都有用到的方法,至於其他就改天再另外介紹。 batch 因為與 react fiber 更新 component 機制有關,這部分較複雜,我對這塊沒有特別研究…,無法提供太多看法。

感謝閱讀,以上有錯誤再麻煩留言或私訊。