useSyncExternalStore

useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?) reads a value React 7,897 does not own — a browser API, a Redux 3,824 store, a third-party cache — and re-renders when it changes. subscribe registers a callback and returns an unsubscribe function; getSnapshot reads the value and must be cheap.

A hook over a browser API that cannot tearJavaScript
import { useSyncExternalStore } from 'react';
function subscribe(onChange) {
  window.addEventListener('online', onChange);
  window.addEventListener('offline', onChange);
  return () => {
    window.removeEventListener('online', onChange);
    window.removeEventListener('offline', onChange);
  };
}
export function useOnlineStatus() {                 // third argument: the value for SSR
  return useSyncExternalStore(subscribe, () => navigator.onLine, () => true);
}

The older pattern — subscribe in an effect, force a re-render by hand — breaks in exactly the case this section is about. A paused render can resume after the external value changed, so components rendered before the pause show the old value and those after the new one. This hook prevents that tearing by re-reading the snapshot at commit time and re-rendering synchronously if it moved.

Two failure modes account for nearly every bug here. If getSnapshot builds a new object on each call — () => ({ count: store.count }) — Object.is never matches and the component re-renders forever; return a primitive or a stable reference. And omitting getServerSnapshot makes server rendering throw, because there is no window to read.

React Redux 9 and Zustand 58,763 call this hook internally, which is how they stay correct under concurrent rendering. Reach for it directly only to wrap a store with no React bindings of its own.