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 的 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 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。
- 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;
- 舊版 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。
這兩個方法 batch
、redux thunk
都是目前專案都有用到的方法,至於其他就改天再另外介紹。 batch 因為與 react fiber 更新 component 機制有關,這部分較複雜,我對這塊沒有特別研究…,無法提供太多看法。
感謝閱讀,以上有錯誤再麻煩留言或私訊。