Skip to content
OVEX TECH
Education & E-Learning

Manage React State Easily with `useSyncExternalStore`

Manage React State Easily with `useSyncExternalStore`

What You’ll Learn

This guide will show you how to use the `useSyncExternalStore` hook in React. You’ll learn how to manage state that comes from outside your React application, like browser events or custom stores. We’ll cover how it simplifies code compared to `useEffect` and show you practical examples, including managing modal visibility and building your own global state management system.

Prerequisites

  • Basic understanding of React and its core concepts.
  • Familiarity with React Hooks like `useState` and `useEffect`.

Understanding the Problem with External State

Sometimes, your React app needs to know about information that lives outside of React itself. This could be something like the browser’s online status, a native HTML element’s state, or data managed by a separate JavaScript library.

Traditionally, you might use the `useEffect` hook to connect this external data to your React component’s state. This involves setting up event listeners to detect changes and then updating your component’s state. However, this approach can become complex, especially when dealing with cleanup logic and preventing unintended state updates from other parts of your code.

For instance, imagine tracking a user’s online status. The browser handles this, and you want your React app to reflect it. Using `useEffect`, you’d add listeners for online/offline events and update a React state variable. The challenge is ensuring this connection is clean, efficient, and doesn’t lead to bugs if state is updated incorrectly elsewhere.

Introducing `useSyncExternalStore`

The `useSyncExternalStore` hook is designed specifically for these situations. It helps you subscribe to external data sources and keep your React components in sync with them. This hook simplifies managing state that originates outside of React, often leading to cleaner and more reliable code than using `useEffect` for the same purpose.

How `useSyncExternalStore` Works

The `useSyncExternalStore` hook takes two main arguments:

  1. `subscribe` function: This function tells React how to listen for changes in the external data source. When the external data changes, this function should trigger a callback.
  2. `getSnapshot` function: This function provides the current value of the external data. React calls this function to get the latest state.

There’s an optional third argument for server-side rendering, providing a default state when the code runs on the server.

Example 1: Tracking Online Status

Let’s refactor the online status example using `useSyncExternalStore`.

Step 1: Define the `subscribe` function

This function will take a callback. Inside, we set up event listeners for the browser’s online and offline events. When these events fire, we call the provided callback to notify React that the external state has changed.

We also need to return a cleanup function that removes these event listeners when the component unmounts, preventing memory leaks.

Step 2: Define the `getSnapshot` function

This function simply returns the current online status by checking `navigator.onLine`. This gives React the most up-to-date value.

Step 3: Use `useSyncExternalStore` in your component

Replace your `useEffect` logic with `useSyncExternalStore`, passing your `subscribe` and `getSnapshot` functions. React will now automatically handle subscribing to changes and updating your component’s state whenever the browser’s online status changes.

Expert Note: This approach eliminates the need for manual cleanup within `useEffect`, making your code more concise and less prone to errors.

Example 2: Managing Modal Visibility

Consider a modal component using the HTML `

` element. Modals often have built-in behavior, like closing when the Escape key is pressed, which might not automatically update your React state.

Step 1: Set up the Modal and Refs

Create your modal using the `

` element and get a reference to it using `useRef`. This reference will allow you to interact with the modal’s methods like `showModal()` and `close()`.

Step 2: Define `getSnapshot` for Modal State

The `getSnapshot` function will check if the modal is currently open. You can access this information through the modal’s properties, often available via `modalRef.current?.open` or similar.

If the modal reference isn’t available yet, provide a default value like `false`.

Step 3: Define `subscribe` for Modal Events

The `

` element fires a `toggle` event when its open state changes (e.g., by pressing Escape). Your `subscribe` function should add an event listener for this `toggle` event on the modal element. When the event occurs, it should call the provided callback to update React’s state.

Ensure you also return a cleanup function to remove the event listener when the component unmounts.

Step 4: Integrate `useSyncExternalStore`

Use `useSyncExternalStore` with your `subscribe` and `getSnapshot` functions. Now, whenever the modal’s visibility changes (whether through your buttons or browser events like Escape), your React component’s state will automatically stay in sync.

Tip: This pattern is incredibly useful for synchronizing React state with any native browser API or DOM element that manages its own state independently.

Example 3: Creating a Custom Global Store

`useSyncExternalStore` is also powerful for building your own state management solutions, similar to libraries like Zustand or Redux.

Step 1: Define Your Store Logic

Create a separate file (e.g., `todoStore.js`) for your store. Inside, define your state (e.g., an array of to-dos) and functions to modify that state (e.g., `addTodo`, `removeTodo`).

Crucially, manage a list of listeners (subscribers) who want to be notified of state changes. A `Set` is a good data structure for this.

Step 2: Implement `subscribe` and `getSnapshot`

In your store file, export a `subscribe` function that accepts a callback. Add this callback to your listeners set. Return a function that removes the callback from the set (for cleanup).

Export a `getSnapshot` function that returns the current state of your store (e.g., the array of to-dos).

Step 3: Notify Listeners on State Change

Whenever you modify the state within your store (e.g., in `addTodo` or `removeTodo`), iterate through your listeners set and call each listener’s callback. This pushes the new state to any components subscribed to the store.

Step 4: Use Your Custom Store in React Components

In your React components, import your `subscribe` and `getSnapshot` functions from the store file. Use `useSyncExternalStore` with these functions to get the current state and subscribe to updates.

You can then use the store’s action functions (like `addTodo`, `removeTodo`) to update the global state. Changes will automatically reflect in all components using the store.

Warning: When updating state in your custom store, ensure you create new references for arrays or objects if you want React to detect the change correctly. For example, instead of directly pushing to an array, create a new array with the added item.

Benefits of `useSyncExternalStore`

  • Simplified State Management: Reduces the complexity of synchronizing external data with React state.
  • Cleaner Code: Often replaces verbose `useEffect` setups with a more declarative approach.
  • Improved Performance: React can optimize re-renders more effectively by understanding the external data flow.
  • Server-Side Rendering Support: Handles server rendering gracefully with its optional third argument.

By using `useSyncExternalStore`, you can build more robust and maintainable React applications, especially when dealing with data sources outside of React’s direct control.


Source: You Need To Start Using This Underrated React Hook (YouTube)

Leave a Reply

Your email address will not be published. Required fields are marked *

Written by

John Digweed

2,543 articles

Life-long learner.