儘可能使用 useReducer,不要使用 useState

原文:useReducer, don't useStatehtml

本文難度:入門級別前端

本文默認你已經大概瞭解過 React Hooks,若是不瞭解能夠先看看 ReactJS 的文檔react

當開發者們開始在他們的應用中使用 React Hooks API 時,不少人一開始都會把 useState 做爲他們的狀態管理工具。 然而,我強烈認爲 useReducer 比 useState 更適合作狀態管理。函數

首先我來定義一下『更適合』是什麼意思:工具

  • 更容易管理大量狀態
  • 更容易被其餘開發者理解
  • 更容易被測試

接下來我分別對三點進行闡述。測試

管理大量狀態

這篇文章大部分觀點只是個人主觀見解。spa

useState 有一個與 class 組件裏面的 setState 明顯不一樣的地方,那就是 useState 不對狀態作淺層合併了,而 useReducer 會合並。code

爲了說明這一點,我這裏給一個使用 useReducer 來實現『撤銷/重作』的例子:htm

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 是多麼地方便,這也引出了第二點。blog

譯註:若是用 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 更好維護。

相關文章
相關標籤/搜索