dreamstate
Version:
Store management library based on react context and observers
78 lines (75 loc) • 3.55 kB
JavaScript
import { useMemo, useEffect, createElement } from 'react';
import { useForceUpdate } from '../../utils/useForceUpdate.js';
/**
* A component that provides data for a specific context manager within an isolated React node.
*
* This component is responsible for observing changes within a specific scope and providing the
* context data for the given context manager (`ManagerClass`). It allows for isolated data provisioning
* and is useful for managing specific parts of the application state while preventing unnecessary
* re-renders of unrelated components.
*
* @template T - The type of the context data to be provided by the `ManagerClass`.
* @param {object} props - The props for the component.
* @param {ReactNode} props.children - The child components that will receive the provided context data.
* @param {TAnyObject | undefined} props.initialState - The initial state to be provided for the context manager.
* @param {Array<unknown>} props.dependencies - The dependencies used to determine when the component should
* re-render, typically when specific context data changes.
* @param {TAnyContextManagerConstructor} props.ManagerClass - The context manager class reference that will provide
* the data to the component.
* @param {string} props.scope - The scope information that defines the context and its relationship to other parts
* of the application.
* @returns {ReactElement} A React element that provides the context data for the specified `ManagerClass`
* within the defined scope.
*/
function ScopedProvider(_a) {
var children = _a.children,
initialState = _a.initialState,
dependencies = _a.dependencies,
ManagerClass = _a.ManagerClass,
scope = _a.scope;
var forceUpdate = useForceUpdate();
/*
* Use memo for first and single init of required components.
* The point is to force registering before provider rendering for first init.
*
* Registering will work in the same way even if it is called multiple times.
* Dependencies array is mostly used for HMR updates to force reloading on class reference changes.
*/
useMemo(function () {
scope.INTERNAL.registerManager(ManagerClass, initialState);
}, dependencies);
/*
* Mount current observers and trigger related lifecycle methods when needed.
* Count references of providers to detect whether we start provisioning or ending it.
*
* ! Dependencies array is mostly used for HMR updates to force reloading on class reference changes.
*/
useEffect(function () {
var isRegistered = scope.INTERNAL.registerManager(ManagerClass, initialState);
scope.INTERNAL.addServiceObserver(ManagerClass, forceUpdate);
/*
* Re-sync scope provider if it was registered.
* Normally happens with HMR chunks exchange and caused problems that use react context without subscription.
* Required to force render of subscribed components after HMR with newest fast-refresh plugins.
*/
if (isRegistered) {
forceUpdate();
}
return function () {
scope.INTERNAL.removeServiceObserver(ManagerClass, forceUpdate);
};
}, dependencies);
/*
* Create simple react provider based on current ManagerClass and provide data from scope registry.
*/
return createElement(ManagerClass.REACT_CONTEXT.Provider, {
value: scope.INTERNAL.REGISTRY.CONTEXT_STATES_REGISTRY.get(ManagerClass)
}, children);
}
/*
* Easier devtools usage for dev environment.
*/
{
ScopedProvider.displayName = "Dreamstate.ScopedProvider";
}
export { ScopedProvider };