UNPKG

dreamstate

Version:

Store management library based on react context and observers

57 lines (54 loc) 2.63 kB
import { useContext, useState, useEffect } from 'react'; import { ScopeContext } from '../scoping/ScopeContext.js'; /** * A custom hook that subscribes to context updates with memoization. * * This hook functions similarly to the standard `useContext` hook but adds memoization based on a dependency selector. * It is particularly useful when a context manager contains a large or frequently changing state, * yet a component only requires updates for specific parts of that state. * * @template T - The type of the context state object. * @template D - The type of the context manager constructor that provides the context state. * @param {D} ManagerClass - The class constructor for the context manager which supplies the context state. * @param {(context: T) => unknown[]} dependenciesSelector - A selector of dependencies from the context state. * The hook will re-render the component only when these selected dependencies change. * @returns {T} The current context state, memoized based on the provided dependencies. */ function useContextWithMemo(ManagerClass, dependenciesSelector) { var scope = useContext(ScopeContext); var state = useState(function () { return scope.INTERNAL.REGISTRY.CONTEXT_STATES_REGISTRY.get(ManagerClass); }); /* * Fire state change only if any of dependencies is updated. */ useEffect(function () { var initialState = state[0]; var setState = state[1]; var subscriptionState = scope.INTERNAL.REGISTRY.CONTEXT_STATES_REGISTRY.get(ManagerClass) || null; // Flag `null` if HMR/StrictMode reset happen, usually just means HMR manager replacing or react 18 strict mode. var observed = subscriptionState ? dependenciesSelector(subscriptionState) : null; /* * Expected to be skipped first time, when state is picked with selector from registry. * Expected to be fired every time ManagerClass is changed - when HMR is called (state is same, effect triggered). */ if (initialState !== subscriptionState) { setState(subscriptionState); } return scope.INTERNAL.subscribeToManager(ManagerClass, function (nextContext) { if (!observed) { observed = dependenciesSelector(nextContext); return setState(nextContext); } var nextObserved = dependenciesSelector(nextContext); for (var it_1 = 0; it_1 < nextObserved.length; it_1++) { if (observed[it_1] !== nextObserved[it_1]) { observed = nextObserved; return setState(nextContext); } } }); }, [ManagerClass, scope.INTERNAL]); return state[0]; } export { useContextWithMemo };