本文難度:入門級別
本文預設你已經大概瞭解過 React Hooks,如果不瞭解可以先看看 ReactJS 的文件。
當開發者們開始在他們的應用中使用 React Hooks API 時,很多人一開始都會把 useState 作為他們的狀態管理工具。 然而,我強烈認為 useReducer 比 useState 更適合做狀態管理。
首先我來定義一下『更適合』是什麼意思:
- 更容易管理大量狀態
- 更容易被其他開發者理解
- 更容易被測試
接下來我分別對三點進行闡述。
管理大量狀態
這篇文章大部分觀點只是我的主觀看法。
useState 有一個與 class 元件裡面的 setState 明顯不同的地方,那就是 useState 不對狀態做淺層合併了,而 useReducer 會合並。
為了說明這一點,我這裡給一個使用 useReducer 來實現『撤銷/重做』的例子:
function init(initialState) {
return {
past: [],
present: initialState,
future: [],
}
}
function reducer(state, action) {
const { past, future, present } = state
switch (action.type) {
case 'UNDO':
const previous = past[past.length - 1]
const newPast = past.slice(0, past.length - 1)
return {
past: newPast,
present: previous,
future: [present, ...future],
}
case 'REDO':
const next = future[0]
const newFuture = future.slice(1)
return {
past: [...past, present],
present: next,
future: newFuture,
}
default:
return state
}
}
複製程式碼
用 useState 達到相同的效果有點困難,不過也並不是不可能。我只是想告訴裡使用 useReducer 是多麼地方便,這也引出了第二點。
譯註:如果用 useState 來做,只需要把 past / present / future 放到同一個 state 裡面即可,但是會造成程式碼分散。而且 useState 也不推薦你在一個 state 裡放太多東西,因為它不會合並 state,用起來不方便。
更容易被其他開發者理解
在 Web 開發中,我們面對的問題很多時候並不是純技術問題。大部分時候你都要跟其他開發者工作,他們的開發經驗很可能跟你的很不一樣。
由於大部分前端開發者都瞭解過 Redux,所以使用 useReducer 比使用 useState 更能快速獲得收益。其核心概念比如 diapatch 一個 action,使用 reducer 來更新 state,都比 useState 更容易被這些開發者掌握。
還有一點值得注意,那就是即使你目前是一個人在開發一個應用,你也保不齊以後會有其他人接手這份程式碼。
更容易被測試
要論 Hooks RFC、Twitter 裡被討論最多的話題,那就是如何測試 Hooks。我覺得要讓開發者理解測試 Hooks 的最佳實踐,還是要花費一些時間的(譯註:尤其是 useState)。但是如果你使用的是 useReducer,那麼你所有的跟 state 相關的業務邏輯程式碼都可以放到一個單獨的函式裡,跟你的元件分開,非常好測試。
把狀態更新程式碼和渲染邏輯分開,使得你可以把測試程式碼也分成這兩部分。以上面的 reducer 程式碼為例, 我們可以輕鬆地測試撤銷和重做,做法是把 mock 狀態和 action 傳給 reducer 即可,我們甚至不用引入 React!
test('it supports undoing the state', () => {
const state = { past: [{ count: 0 }], present: { count: 1 }, future: [] }
const newState = reducer(state, { type: 'UNDO' })
expect(newState.present.count).toBe(0)
})
複製程式碼
總結
我並不期望大家只使用 useReducer 不使用 useState,我個人也不會這麼做,它們各有各的使用場景。但是我的確認為 useReducer 在複雜的狀態管理場景下比 useState 更好維護。