UNPKG

synapse-storage

Version:

Набор инструментов для управления состоянием и апи-запросами

70 lines (63 loc) 3.21 kB
import { useCallback, useRef, useSyncExternalStore } from "react"; import { logError } from "../../_utils/error-handling.util.js"; /** * Хук для подписки на изменения в хранилище. * Использует useSyncExternalStore для корректной работы в Concurrent Mode. * * Принимает IStorageBase (общий интерфейс для sync и async хранилищ), * так как подписки одинаковы для всех типов. * * @template S - Тип состояния хранилища * @template R - Тип возвращаемого значения * @param storage - Экземпляр хранилища или null (до инициализации) * @param selector - Функция-селектор для выбора данных * @param options - Опции: `equals` для мемоизации снапшота (см. {@link UseStorageSubscribeOptions}) * @returns Значение из хранилища */ const useStorageSubscribe = (storage, selector, options)=>{ // Храним selector в ref, чтобы не пересоздавать subscribe/getSnapshot при изменении ссылки const selectorRef = useRef(selector); selectorRef.current = selector; const equalsRef = useRef(options?.equals); equalsRef.current = options?.equals; // Кеш последнего снапшота — нужен и для мемоизации по equals, и чтобы вернуть // стабильную ссылку (иначе useSyncExternalStore при селекторе-фабрике объекта // зациклится на бесконечном ререндере). const cachedRef = useRef(undefined); const hasCacheRef = useRef(false); const subscribe = useCallback((onStoreChange)=>{ if (!storage) return ()=>{}; // Подписка на все изменения storage + на смену статуса (для момента инициализации) const unsubData = storage.subscribeToAll(()=>onStoreChange()); const unsubStatus = storage.onStatusChange(()=>onStoreChange()); return ()=>{ unsubData(); unsubStatus(); }; }, [ storage ]); const getSnapshot = useCallback(()=>{ if (!storage) return undefined; try { const state = storage.getStateSync(); const value = selectorRef.current(state); const equals = equalsRef.current; if (equals && hasCacheRef.current && equals(cachedRef.current, value)) { return cachedRef.current; } cachedRef.current = value; hasCacheRef.current = true; return value; } catch (error) { if (process.env.NODE_ENV !== 'production') { logError('useStorageSubscribe: selector error', error, null, 'warn'); } return undefined; } }, [ storage ]); return useSyncExternalStore(subscribe, getSnapshot); }; export { useStorageSubscribe }; //# sourceMappingURL=useStorageSubscribe.js.map