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

目前執行的專案,資料都是 linked list,若沒有注意好 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 的 unstablebatchedUpdate,讓 rerender 這件事情能夠被卡住,react 實現方法大致上就是用 shouldBatchUpdates 變數搭配 fiber schedule 來判斷更新,讓更新這件事變成同步,詳細可直接看下方 react unstablebatchedUpdates 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 一次。

// 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;
  • 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 機制有關,這部分較複雜,我對這塊沒有特別研究…,無法提供太多看法。

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