UseReducer, don’t use estate

Difficulty of this article: Entry level

This article assumes that you already know a bit about React Hooks, but if you don’t, you can start by looking at the ReactJS documentation.

When developers start using the React Hooks API in their applications, many start using useState as their state management tool. However, I strongly believe that useReducer is better suited for state management than useState.

First, let me define what “more suitable” means:

  • Easier to manage large numbers of states
  • Easier for other developers to understand
  • Easier to test

Next, I will elaborate on the three points respectively.

Managing a large number of states

Most of the opinions in this article are just my subjective opinions.

One notable difference between useState and setState in the class component is that useState does not shallow merge state, whereas useReducer does merge state.

To illustrate this point, here is an example of using useReducer to implement undo/redo:

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
  }
}
Copy the code

Achieving the same effect with useState is a bit difficult, but not impossible. I just want to show you how convenient it is to use useReducer, which leads to the second point.

If you use useState, you just need to put past/present/future in the same state, but it will cause code fragmentation. Also, useState doesn’t recommend putting too many things in one state, because it doesn’t merge states and is inconvenient to use.

Easier for other developers to understand

Many of the problems we face in Web development are not purely technical. Most of the time you will be working with other developers whose development experience is likely to be very different from yours.

Since most front-end developers are familiar with Redux, using useReducer is a faster way to make money than using useState. Core concepts such as Diapatch, an action, and reducer to update state are easier to grasp than useState.

It’s also important to note that even if you’re currently working on an application alone, it’s highly unlikely that someone else will take over the code.

Easier to test

Hooks RFC, the most talked about topic on Twitter, is how to test Hooks. I think it will take some time for developers to understand the best practices of testing Hooks. But if you use useReducer, all of your state-related business logic code can be placed in a separate function, separate from your component, which is easy to test.

Separating the status update code from the render logic allows you to split the test code into these two parts as well. Using the reducer code above as an example, we can easily test undo and redo by passing the mock state and actions to the Reducer, without even introducing 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)})Copy the code

conclusion

I don’t expect you to use useReducer without useState, nor do I personally. They have their own usage scenarios. But I do think useReducer is more maintenable than useState in complex state management scenarios.