儘量使用 useReducer,不要使用 useState(譯文)
本文難度:入門級別
本文預設你已經大概瞭解過 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 更好維護。