How to Use a Set With React useState() Hook?

In React, managing state is a fundamental aspect of building interactive and dynamic user interfaces. The useState() hook provides a simple and efficient way to manage state in functional components. While it's common to use primitive values or objects as state in React, you can also leverage more complex data structures like sets.

To learn more about using sets with useState(), you can go through the following basics:

  1. Initializing useState() With Set;
  2. Adding and Removing Items;
  3. Rendering Set Contents;
  4. Example.

Sets are a built-in data structure in JavaScript that store unique values. They can be particularly useful when you need to manage a collection of distinct items without worrying about duplicates. Integrating sets with the React useState() hook allows you to maintain such collections within React components.

Initializing useState() With Set

To use a Set with useState(), you can initialize the state with a new instance of a Set, for example, like so:

// initialize state with a new `Set` instance
const [mySet, setMySet] = useState(new Set());

This sets up the initial state to be an empty Set, ready to be populated with values as needed.

Adding and Removing Items

To add or remove items from a Set, you'll typically define functions that use the setter function provided by useState(). It's crucial to use the updater function form to ensure that you're working with the most up-to-date state, as React may batch state updates for performance reasons.

For example, to add a new value to the Set you can do the following:

const addItemToSet = (item) => {
  setMySet((prevSet) => {
    // 1: create a new `Set` based on the previous `Set`
    const newSet = new Set(prevSet);
    // 2: add the item to the new `Set`
    newSet.add(item);
    // 3: return the new `Set`
    return newSet;
  });
};

In this example, a new Set is created based on the Set from the previous render. To remove an item, you can do something similar, as shown below:

const removeItemFromSet = (item) => {
  setMySet((prevSet) => {
    // 1: create a new `Set` based on the previous `Set`
    const newSet = new Set(prevSet);
    // 2: remove the item from the new `Set`
    newSet.delete(item);
    // 3: return the new `Set`
    return newSet;
  });
};

Why Does a New Set Need to be Created Each Time?

This approach is beneficial for the following reasons:

  1. Immutability: React encourages immutability in state management. By creating a new Set each time, you're not mutating the existing state directly. This helps prevent unintended side effects by improving code readability and predictability, leading to easier maintenance and debugging.
  2. State Updates: React uses reference equality to detect changes in state. If you modify the existing Set directly (for example, by calling add or delete on the current Set), React may not detect the change because the reference to the Set remains the same. By creating a new Set, you ensure that the reference changes, signaling to React that the state has been updated — you can see this demonstrated in the useEffect() hook used in the example.
  3. Functional Updates: React's state updater functions (like setState in class components and the updater function returned by useState() in functional components) are designed to work with immutable updates. They accept either a new state value or a function that returns the new state based on the previous state. By providing a function that creates a new Set based on the previous Set, you adhere to this functional update pattern.

While creating a new Set every time may seem inefficient, modern JavaScript engines are optimized for such operations, and the performance impact is typically negligible for most applications. Additionally, the benefits of immutability and predictable state management outweigh any minor performance considerations.

Rendering Set Contents

To display the contents of the Set in your component, you can, for example, use the Array.from() method to convert the Set to an array, and then iterate over it using Array.prototype.map():

<div>
  {Array.from(mySet).map((item) => (
    <div key={item}>{item}</div>
  ))}
</div>

Example

You can view the complete working example below:

import React, { useState, useEffect } from 'react';

function MyComponent() {
  const [mySet, setMySet] = useState(new Set());

  useEffect(() => {
    console.log('Set was updated', mySet);
  }, [mySet]);

  const addItemToSet = (item) => {
    setMySet((prevSet) => {
      const newSet = new Set(prevSet);
      newSet.add(item);
      return newSet;
    });
  };

  const removeItemFromSet = (item) => {
    setMySet((prevSet) => {
      const newSet = new Set(prevSet);
      newSet.delete(item);
      return newSet;
    });
  };

  return (
    <div>
      <button onClick={() => addItemToSet('foo')}>Add Foo</button>
      <button onClick={() => addItemToSet('bar')}>Add Bar</button>
      <button onClick={() => removeItemFromSet('foo')}>Remove Foo</button>
      <button onClick={() => removeItemFromSet('bar')}>Remove Bar</button>
      <div>
        {Array.from(mySet).map((item) => (
          <div key={item}>{item}</div>
        ))}
      </div>
    </div>
  );
}

export default MyComponent;

In this example:

  1. The state "mySet" is initialized to an empty Set using useState();
  2. The "addItemToSet()" and "removeItemFromSet()" functions are defined to add and remove values from the Set respectively;
  3. These add/remove functions create a copy of the current Set, perform the necessary operation (i.e., adding or removing), and then update the state with the new Set using "setMySet()";
  4. In the JSX, the buttons call addItemToSet() and removeItemFromSet() functions to demonstrate adding and removing values from the set;
  5. Lastly, Array.from() is used to convert Set to array to iterate over it and render its values.

This post was published by Daniyal Hamid. Daniyal currently works as the Head of Engineering in Germany and has 20+ years of experience in software engineering, design and marketing. Please show your love and support by sharing this post.