尽可能使用 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 更好维护。

相关文章
相关标签/搜索