MENU
useSyncExternalStore
useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?) reads a value from a store that lives outside React, and re-renders the component whenever that store changes. subscribe takes a callback, hooks it up to the store's own change notifications, and returns an unsubscribe function; call the callback whenever the store changes, and React re-runs getSnapshot to check for an update. getSnapshot just reads and returns the store's current value – cheaply, since it may be called often – and must keep returning an equal value (compared with Object.is) for as long as nothing has actually changed, or React will conclude the store changed on every check and re-render in a loop.
The optional third argument, getServerSnapshot, supplies the value to use during server rendering and hydration. It isn't needed for the client-only demo below, but leaving it out does make server rendering throw for a component that calls this Hook.
This Hook is React's answer to a problem older than Hooks themselves: safely reading mutable data that React doesn't own – a browser API, a third-party store, an old Backbone model – from inside a render. The previous way of doing this was to subscribe inside a useEffect and force a re-render by hand; that works most of the time, but under React's concurrent rendering a store can mutate mid-render and leave different parts of the tree reading different values, a class of bug usually called "tearing". useSyncExternalStore is built specifically to avoid it.
RESETRUNFULL
<!DOCTYPE html><html><head>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head><body>
<div></div>
<script type="text/babel">
function subscribe(callback) {
window.addEventListener('online', callback);
window.addEventListener('offline', callback);
return () => {
window.removeEventListener('online', callback);
window.removeEventListener('offline', callback);
};
}
function getSnapshot() {
return navigator.onLine;
}
function useOnlineStatus() {
return React.useSyncExternalStore(subscribe, getSnapshot);
}
function App() {
const isOnline = useOnlineStatus();
return <p>Status: {isOnline ? 'Online' : 'Offline'}</p>;
}
const root = ReactDOM.createRoot(document.querySelector("div"));
root.render(<App/>);
</script>
</body></html>