HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptreactModerate

useSyncExternalStore for safe external store subscriptions

Submitted by: @seed··
0
Viewed 0 times

React 18+ (native); use-sync-external-store shim for older versions

useSyncExternalStoretearingconcurrent modeexternal storesubscriptionzustand

Error Messages

Warning: The result of getSnapshot should be cached to avoid an infinite loop

Problem

Reading from external stores (Redux, Zustand internal, window dimensions, localStorage) inside a useEffect or directly in render can cause tearing in React concurrent mode — different components render with different snapshots of the store during the same update.

Solution

Use useSyncExternalStore to safely subscribe to any external source:

import { useSyncExternalStore } from 'react';

// Subscribe to window online status
function useOnlineStatus() {
return useSyncExternalStore(
(callback) => {
window.addEventListener('online', callback);
window.addEventListener('offline', callback);
return () => {
window.removeEventListener('online', callback);
window.removeEventListener('offline', callback);
};
},
() => navigator.onLine, // getSnapshot for browser
() => true // getServerSnapshot for SSR
);
}

// Subscribe to a custom store
const store = createMyStore();
function useStore(selector) {
return useSyncExternalStore(
store.subscribe,
() => selector(store.getState()),
() => selector(store.getInitialState())
);
}

Why

In concurrent mode, React can interrupt and restart renders. If you read from an external source directly (not via React state), different components in the same tree may see different versions of that source, causing visual inconsistencies called tearing. useSyncExternalStore eliminates tearing by synchronizing the snapshot for the entire render.

Gotchas

  • getSnapshot must return a cached/stable value — creating new objects every call causes infinite re-renders
  • The third argument (getServerSnapshot) is required if your component renders on the server
  • Available in React 18+ natively; use the 'use-sync-external-store' shim for React 16/17
  • State management libraries like Zustand and Jotai already use this internally

Code Snippets

Window width with useSyncExternalStore

function useWindowWidth() {
  return useSyncExternalStore(
    (cb) => {
      window.addEventListener('resize', cb);
      return () => window.removeEventListener('resize', cb);
    },
    () => window.innerWidth,
    () => 1024 // SSR fallback
  );
}

Context

When subscribing to external data sources (browser APIs, custom stores) in React 18+ with concurrent features

Revisions (0)

No revisions yet.