Capítulo 64 de 116

Chapter 64: useSyncExternalStore

Core Idea

useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?) is the correct way to read a value from a store outside React (a third-party state library, a browser API) that can change on its own — it guarantees the component re-renders on every store change and never tears (shows inconsistent values) even under concurrent rendering.

Key Concepts

  • Signature: subscribe(callback) registers callback to run whenever the store changes and returns an unsubscribe function; getSnapshot() returns the store's current value (must return a cached/stable reference if nothing changed, not a fresh object every call); optional getServerSnapshot() provides the value to use during server rendering.
  • Why not just useEffect + useState: subscribing manually via useEffect and mirroring the external value into useState is a common pattern for external stores, but it can "tear" under React's concurrent rendering — different parts of the tree could momentarily read different values of the same external store mid-render. useSyncExternalStore is specifically built to prevent that.
  • getSnapshot must be cheap and stable: called on every render (and more, internally, for tear-checking) — it must return the same reference if the underlying data hasn't changed, or the Hook will think the store changed and force extra re-renders.
  • This is primarily a library-author-facing Hook. Most application code interacts with external stores through a library that already calls useSyncExternalStore internally (state management libraries, routers) — writing a raw call by hand is comparatively rare in app code.
  • getServerSnapshot is required for SSR-rendered components reading an external store — without it, React throws during server rendering since there's no way to know what value to use before the client's store exists.

Code Examples

function useOnlineStatus() {
  return useSyncExternalStore(
    (callback) => {
      window.addEventListener('online', callback);
      window.addEventListener('offline', callback);
      return () => {
        window.removeEventListener('online', callback);
        window.removeEventListener('offline', callback);
      };
    },
    () => navigator.onLine,       // client snapshot
    () => true                     // server snapshot
  );
}
  • What it demonstrates: subscribing to a genuinely external, browser-owned store (navigator.onLine) safely, with a server-side fallback snapshot.

Key Takeaways

  1. Reach for this specifically for external (non-React-owned) mutable stores, not for React state or Context.
  2. getSnapshot returning a fresh object/array every call is a common bug — it must be referentially stable when the store hasn't changed.
  3. Most app code consumes this indirectly through a state-management library rather than calling it directly.

Connects To

  • Ch 41 (Reusing Logic with Custom Hooks): the custom-Hook wrapper style shown here (useOnlineStatus).
  • Ch 36 (Synchronizing with Effects): the manual useEffect-based alternative this Hook improves on for tear-safety.