Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Micro State Management with React Hooks

You're reading from   Micro State Management with React Hooks Explore custom hooks libraries like Zustand, Jotai, and Valtio to manage global states

Arrow left icon
Product type Paperback
Published in Feb 2022
Publisher Packt
ISBN-13 9781801812375
Length 254 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Daishi Kato Daishi Kato
Author Profile Icon Daishi Kato
Daishi Kato
Arrow right icon
View More author details
Toc

Table of Contents (16) Chapters Close

Preface 1. Part 1: React Hooks and Micro State Management
2. Chapter 1: What Is Micro State Management with React Hooks? FREE CHAPTER 3. Part 2: Basic Approaches to the Global State
4. Chapter 2: Using Local and Global States 5. Chapter 3: Sharing Component State with Context 6. Chapter 4: Sharing Module State with Subscription 7. Chapter 5: Sharing Component State with Context and Subscription 8. Part 3: Library Implementations and Their Uses
9. Chapter 6: Introducing Global State Libraries 10. Chapter 7: Use Case Scenario 1 – Zustand 11. Chapter 8: Use Case Scenario 2 – Jotai 12. Chapter 9: Use Case Scenario 3 – Valtio 13. Chapter 10: Use Case Scenario 4 – React Tracked 14. Chapter 11: Similarities and Differences between Three Global State Libraries 15. Other Books You May Enjoy

Using useReducer

In this section, we will learn how to use useReducer. We will learn about its typical usage, how to bail out, its usage with primitive values, and lazy initialization.

Typical usage

A reducer is helpful for complex states. Here's a simple example a with two-property object:

const reducer = (state, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count + 1 };
    case 'SET_TEXT':
      return { ...state, text: action.text };
    default:
      throw new Error('unknown action type');
  }
};
const Component = () => {
  const [state, dispatch] = useReducer(
    reducer,
    { count: 0, text: 'hi' },
  );
  return (
    <div>
      {state.count}
      <button
        onClick={() => dispatch({ type: 'INCREMENT' })}
      >
        Increment count
      </button>
      <input
        value={state.text}
        onChange={(e) =>
          dispatch({ type: 'SET_TEXT', text: e.target.value })}
      />
    </div>
  );
};

useReducer allows us to define a reducer function in advance by taking the defined reducer function and initial state in parameters. The benefit of defining a reducer function outside the hook is being able to separate code and testability. Because the reducer function is a pure function, it's easier to test its behavior.

Bailout

As well as useState, bailout works with useReducer too. Using the previous example, let's modify the reducer so that it will bail out if action.text is empty, as follows:

const reducer = (state, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count + 1 };
    case 'SET_TEXT':
      if (!action.text) {
        // bail out
        return state
      }
      return { ...state, text: action.text };
    default:
      throw new Error('unknown action type');
  }
};

Notice that returning state itself is important. If you return { ...state, text: action.text || state.text } instead, it won't bail out because it's creating a new object.

Primitive value

useReducer works for non-object values, which are primitive values such as numbers and strings. useReducer with primitive values is still useful as we can define complex reducer logic outside it.

Here is a reducer example with a single number:

const reducer = (count, delta) => {
  if (delta < 0) {
    throw new Error('delta cannot be negative');
  }
  if (delta > 10) {
    // too big, just ignore
    return count
  }
  if (count < 100) {
    // add bonus
    return count + delta + 10
  }
  return count + delta
}

Notice that the action (= delta) doesn't have to have an object either. In this reducer example, the state value is a number—a primitive value—but the logic is a little more complex, with more conditions than just adding numbers.

Lazy initialization (init)

useReducer requires two parameters. The first is a reducer function and the second is an initial state. useReducer accepts an optional third parameter, which is called init, for lazy initialization.

For example, useReducer can be used like this:

const init = (count) => ({ count, text: 'hi' });
const reducer = (state, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count + 1 };
    case 'SET_TEXT':
      return { ...state, text: action.text };
    default:
      throw new Error('unknown action type');
  }
};
const Component = () => {
  const [state, dispatch] = useReducer(reducer, 0, init);
  return (
    <div>
      {state.count}
      <button
        onClick={() => dispatch({ type: 'INCREMENT' })}
      >
        Increment count
      </button>
      <input
        value={state.text}
        onChange={(e) => dispatch({ 
          type: 'SET_TEXT', 
          text: e.target.value,
        })}
      />
    </div>
  );
};

The init function is invoked just once on mount, so it can include heavy computation. Unlike useState, the init function takes a second argument—initialArg—in useReducer, which is 0 in the previous example.

Now we have looked at useState and useReducer separately, it's time to compare them.

You have been reading a chapter from
Micro State Management with React Hooks
Published in: Feb 2022
Publisher: Packt
ISBN-13: 9781801812375
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image