useSyncExternalStoreHook
useSyncExternalStore Hook β React's connection to external stores
useSyncExternalStore Hook β Connecting React to External Stores
React 18 introduced a new hook β useSyncExternalStore, designed to safely and stably connect React to any external store or data source. It ensures that React components synchronize with the latest data value without race conditions.
As a result, React always renders with consistent data, even if the store changes during rendering. This is the hook that modern libraries like Redux, Zustand, jotai, Recoil are built upon.
πΉ Syntax
const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)
- subscribe β function that notifies React when the store changes.
- getSnapshot β returns the current value of the store.
- getServerSnapshot β optional, used to provide server-side value during SSR.
π Example 1 β Simple Custom Store
Let's create a small JavaScript store that stores a value and allows subscribing to changes.
// store.js
let count = 0;
let listeners = new Set();
export const store = {
getSnapshot() {
return count;
},
subscribe(callback) {
listeners.add(callback);
return () => listeners.delete(callback);
},
increment() {
count++;
listeners.forEach((cb) => cb());
}
};
Here we have a simple counter store that can: β store the value (`count`) β notify all subscribers when count changes
Usage in React Component
// Counter.js
import React from 'react';
import { store } from './store';
import { useSyncExternalStore } from 'react';
export default function Counter() {
const count = useSyncExternalStore(
store.subscribe,
store.getSnapshot
);
return (
<div>
<h2>Count: {count}</h2>
<button onClick={store.increment}>+1</button>
</div>
);
}
In this case, React always gets the latest value from the `store` and only re-renders when the store actually changes. This is much more efficient and secure than using `useEffect` or `useState` for store synchronization.
π Why useSyncExternalStore is Needed
Previously, many libraries (e.g., older versions of Redux) used useEffect to get data from the store and notify React. However, during concurrent rendering, this caused race conditions β when React would render with old values.
useSyncExternalStore solves this problem by guaranteeing that during rendering, React always uses the latest consistent snapshot.
π Example 2 β Connection with Socket or API
For example, you have a websocket receiving real-time data. useSyncExternalStore can be used for React components to get the latest values without re-render lag.
import React, { useSyncExternalStore } from 'react';
function createSocketStore() {
let data = 'waiting...';
let listeners = new Set();
const ws = new WebSocket('wss://example.com/socket');
ws.onmessage = (e) => {
data = e.data;
listeners.forEach((cb) => cb());
};
return {
subscribe(cb) {
listeners.add(cb);
return () => listeners.delete(cb);
},
getSnapshot() {
return data;
}
};
}
const socketStore = createSocketStore();
export default function LiveData() {
const value = useSyncExternalStore(
socketStore.subscribe,
socketStore.getSnapshot
);
return <div>π‘ Latest data: {value}</div>;
}
Here React receives live data from the socket, but always consistently β without the risk of rendering reaching old values.
π Example 3 β getServerSnapshot in SSR
If you're using a store in SSR projects (e.g., Next.js), you can provide a server-side snapshot β for example, a default value.
const count = useSyncExternalStore(
store.subscribe,
store.getSnapshot,
() => 0 // Server-side default
);
This way React won't complain about server and client snapshots being different during hydration.
βοΈ Best Practices
- Use it when React needs to synchronize with external data sources.
- Don't use useEffect or useState for store sync β it's an outdated approach in concurrent rendering.
- If you're creating a library (e.g., custom state manager), build it on useSyncExternalStore.
- Create stable subscribe/getSnapshot pairs to avoid race conditions.
π― Exercise (Try it yourself)
Write a small store that:
- Stores a counter value.
- Has subscribe(callback) and getSnapshot() methods.
- Use useSyncExternalStore in a React component to display and modify it.
Try doing the same with useEffect/useState version and see the difference β in render consistency and re-render speed.
π Summary
- useSyncExternalStore β React hook for consistent connection with external data sources.
- Used in state systems like Redux, Zustand, Firebase, and sockets.
- Aligns with the new concurrent rendering logic.
- Ensures consistent snapshot and prevents race conditions.
- 0
- 3