dreamstate
Version:
Store management library based on react context and observers
57 lines (54 loc) • 2.63 kB
JavaScript
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 };