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

Working with useState

In this section, we will learn how to use useState, from basic usage to advanced usage. We start with the simplest form, which is updating with the state with a new value, then updating with a function, which is a very powerful feature, and finally, we will discuss lazy initialization.

Updating the state value with a value

One way to update the state value with useState is by providing a new value. You can pass a new value to the function returned by useState that will eventually replace the state value with the new value.

Here is a counter example showing updating with a value:

const Component = () => {
  const [count, setCount] = useState(0);
  return (
    <div>
      {count}
      <button onClick={() => setCount(1)}>
        Set Count to 1
      </button>
    </div>
  );
};

You pass a value of 1 to setCount in the onClick handler. If you click the button, it will trigger Component to re-render with count=1.

What would happen if you clicked the button again? It will invoke setCount(1) again, but as it is the same value, it "bails out" and the component won't re-render. Bailout is a technical term in React and basically means avoiding triggering re-renders.

Let's look at another example here:

const Component = () => {
  const [state, setState] = useState({ count: 0 });
  return (
    <div>
      {state.count}
      <button onClick={() => setState({ count: 1 })}>
        Set Count to 1
      </button>
    </div>
  );
};

This behaves exactly the same as the previous example for the first click; however, if you click the button again, the component will re-render. You don't see any difference on screen because the count hasn't changed. This happens because the second click creates a new object, { count: 1 }, and it's different from the previous object.

Now, this leads to the following bad practice:

const Component = () => {
  const [state, setState] = useState({ count: 0 });
  return (
    <div>
      {state.count}
      <button
        onClick={() => { state.count = 1; setState(state); }
      >
        Set Count to 1
      </button>
    </div>
  );
};

This doesn't work as expected. Even if you click the button, it won't re-render. This is because the state object is referentially unchanged, and it bails out, meaning this alone doesn't trigger the re-render.

Finally, there's an interesting usage of value update, which we can see here:

const Component = () => {
  const [count, setCount] = useState(0);
  return (
    <div>
      {count}
      <button onClick={() => setCount(count + 1)}>
        Set Count to {count + 1}
      </button>
    </div>
  );
};

Clicking the button will increment the count; however, if you click the button twice quickly enough, it will increment by just one number. This is sometimes desirable as it matches with the button title, but sometimes it's not if you expect to count how many times the button is actually clicked. That requires a function update.

Updating the state value with a function

Another way to update the state with useState is called a function update.

Here is a counter example showing updating with a function:

const Component = () => {
  const [count, setCount] = useState(0);
  return (
    <div>
      {count}
      <button onClick={() => setCount((c) => c + 1)}>
        Increment Count
      </button>
    </div>
  );
};

This actually counts how many times the button is clicked, because (c) => c + 1 is invoked sequentially. As we saw in the previous section, value update has the same use case as the Set Count to {count + 1} feature. In most use cases, function updates work better if the update is based on the previous value. The Set Count to {count + 1} feature actually means that it doesn't depend on the previous value but depends on the displayed value.

Bailout is also possible with function updates. Here's an example to demonstrate this:

const Component = () => {
  const [count, setCount] = useState(0);
  useEffect(() => {
    const id = setInterval(
      () => setCount((c) => c + 1),
      1000,
    );
    return () => clearInterval(id);
  }, []);
  return (
    <div>
      {count}
      <button
        onClick={() =>
          setCount((c) => c % 2 === 0 ? c : c + 1)}
      >
        Increment Count if it makes the result even
      </button>
    </div>
  );
};

If the update function returns the exact same state as the previous state, it will bail out, and this component won't re-render. For example, if you invoke setCount((c) => c), it will never re-render.

Lazy initialization

useState can receive a function for initialization that will be evaluated only in the first render. We can do something like this:

const init = () => 0;
const Component = () => {
  const [count, setCount] = useState(init);
  return (
    <div>
      {count}
      <button onClick={() => setCount((c) => c + 1)}>
        Increment Count
      </button>
    </div>
  );
};

The use of init in this example is not very effective because returning 0 doesn't require much computation, but the point is that the init function can include heavy computation and is only invoked to get the initial state. The init function is evaluated lazily, not evaluated before calling useState; in other words, it's invoked just once on mount.

We have now learned how to use useState; next up is useReducer.

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