Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама
Senior Хуки

Навіщо useSyncExternalStore і як підписатися на зовнішнє джерело даних?

Більшість стану живе в React (useState, useReducer). Але інколи дані зберігаються зовні: у сторонній бібліотеці стану, в API браузера (navigator.onLine, matchMedia, localStorage), у власному класі-сховищі.

Наївний підхід - useEffect з підпискою і копією в useState - має проблеми в конкурентному рендерингу: React може рендерити частини дерева в різний час, і різні компоненти побачать різні версії зовнішнього значення («розрив», tearing).

useSyncExternalStore - офіційний спосіб читати зовнішнє джерело узгоджено:

import { useSyncExternalStore } from 'react';

function subscribe(callback) {
  window.addEventListener('online', callback);
  window.addEventListener('offline', callback);
  return () => {
    window.removeEventListener('online', callback);
    window.removeEventListener('offline', callback);
  };
}

export function useOnlineStatus() {
  return useSyncExternalStore(
    subscribe,
    () => navigator.onLine,   // знімок на клієнті
    () => true,               // знімок для серверного рендеру
  );
}

Три аргументи:

  1. subscribe(callback) - підписатися на зміни й повернути функцію відписки;
  2. getSnapshot() - поточне значення;
  3. getServerSnapshot() - значення для серверного рендеру й гідратації (на сервері немає navigator).

Ключові правила:

  • getSnapshot має повертати те саме значення, доки дані не змінилися. Якщо він щоразу створює новий об'єкт (() => ({ ...store.state })), React вважатиме, що дані змінилися на кожному рендері, - нескінченний цикл і помилка. Повертайте збережене незмінне значення або примітив;
  • subscribe - стабільна функція, оголошена поза компонентом (чи в useCallback), інакше React перепідписуватиметься на кожному рендері;
  • оновлення з такого джерела не можна позначити як transition - вони завжди синхронні.

Хто вже використовує: Redux (useSelector), Zustand, TanStack Query та інші бібліотеки стану побудовані на ньому. У застосунку цей хук зазвичай потрібен для API браузера чи власного сховища.

Приклад із localStorage: підписка на подію storage (зміни з інших вкладок) плюс власна подія для змін у поточній вкладці - і стан синхронізований між вкладками.

Докладніше в документації: useSyncExternalStore

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

Схожі питання