UNPKG

synapse-storage

Version:

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

75 lines (70 loc) 3.37 kB
import { useCallback, useRef, useSyncExternalStore } from "react"; function useSelector(selector, options) { const equalsRef = useRef(options?.equals); // Кеш для мемоизации результата getSnapshot (предотвращает лишние ререндеры) const cachedRef = useRef(undefined); const subscribe = useCallback((onStoreChange)=>{ return selector.subscribe({ notify: ()=>{ onStoreChange(); } }); }, [ selector ]); const getSnapshot = useCallback(()=>{ const value = selector.selectSync(); // Если есть пользовательская функция сравнения — мемоизируем const equals = equalsRef.current; if (equals && cachedRef.current !== undefined && equals(cachedRef.current, value)) { return cachedRef.current; } cachedRef.current = value; return value; }, [ selector ]); // getServerSnapshot === getSnapshot: на сервере `selectSync()` синхронно читает // засеянный (hydrate) стор. Без серверного снапшота useSyncExternalStore на сервере // падает и откатывается на client-only рендер (контент не попадает в SSR-HTML). const value = useSyncExternalStore(subscribe, getSnapshot, getSnapshot); // Подписка на статус готовности storage (используется только при withLoading) const subscribeToStatus = useCallback((onStoreChange)=>{ return selector.onSourceStatusChange(()=>{ onStoreChange(); }); }, [ selector ]); const getStatusSnapshot = useCallback(()=>{ return !selector.isSourceReady(); }, [ selector ]); const isLoading = useSyncExternalStore(subscribeToStatus, getStatusSnapshot, getStatusSnapshot); if (options?.withLoading) { return { data: value, isLoading }; } return value; } /** * Изоляция ре-рендеров для keyed-map стора (паттерн «equals»). Подписка идёт на * ВЕСЬ map (`selector`), но `equals` сравнивает только срез по `key` → компонент * ре-рендерится лишь когда меняется его `map[key]` по ссылке. Работает, т.к. * иммутабельные мутации по одному ключу не трогают ссылку чужих срезов. * Гранулярность живёт в месте вызова, а селекторы остаются плоскими (без фабрик * с `Map<key, selector>`). * * `fallback` ОБЯЗАН быть стабильной ссылкой (module-level константа), иначе * `?? fallback` будет давать новый объект каждый тик и рвать нижестоящие `useMemo`. */ function useKeyedSliceSelector(selector, key, fallback) { const map = useSelector(selector, { equals: (a, b)=>a[key] === b[key] }); return map[key] ?? fallback; } export { useKeyedSliceSelector, useSelector }; //# sourceMappingURL=useSelector.js.map