fluidstate-react
Version:
Library for using fine-grained reactivity state management library fluidstate in React
170 lines (157 loc) • 6.53 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createReactiveSetup = void 0;
var _react = require("react");
var _hoc = require("./hoc");
var _hooks = require("./hooks");
var _onceRef = require("./once-ref");
var _jsxRuntime = require("react/jsx-runtime");
/**
* A phantom symbol used to associate a `State` type with a component type.
* This enables utility types like `StateType` to extract the state type
* from the component type itself, a technique similar to nominal typing.
*/
/**
* A React Provider component for providing mock state, typically used in tests
* or Storybook. It provides a partial state object via its `value` prop to its children.
* @template State The full state type.
*/
/**
* The core type definition for the ReactiveProvider component.
* @private
*/
/**
* A React Provider component that creates and provides reactive state to its children.
* It ensures that the state is created only once per component instance and automatically
* manages the lifecycle of any reactions defined in the state.
*
* It also carries a phantom `State` type, allowing utility types to infer the state shape.
*
* @template SetupProps The type of props required to initialize the state.
* @template State The type of the reactive state object.
*/
/**
* A hook for consuming reactive state within a component.
* It subscribes to changes in the slice of state returned by `readReactiveState`
* and re-renders the component when that data changes.
*
* @template State The type of the global reactive state.
* @param readReactiveState A function that selects a part of the state to subscribe to.
* @returns The value returned by `readReactiveState`.
*/
/**
* A higher-order component (HOC) that injects reactive state into a component as a `state` prop.
* The wrapped component will automatically re-render when the reactive state it uses changes.
*
* @template State The type of the global reactive state.
* @param component The component to wrap.
* @returns A new component that receives the reactive state.
*/
/**
* A factory function type for creating the reactive state.
* It receives `setupProps` and is expected to return the initial state object.
*
* @template SetupProps The type of props for state initialization.
* @template State The type of the reactive state object.
*/
/**
* A utility type that extracts the `State` type from a `ReactiveProvider` type.
* @template T The `ReactiveProvider` type.
*/
/**
* A utility type that extracts the `SetupProps` type from a `ReactiveProvider` type.
* @template T The `ReactiveProvider` type.
*/
/**
* A utility type that extracts the `CreateState` function type from a `ReactiveProvider` type.
* @template T The `ReactiveProvider` type.
*/
/**
* An object containing the complete set of utilities for a specific reactive state setup.
* This includes the provider, consumer hook, HOC, mock provider, and state factory.
*/
/**
* Creates a cohesive set of tools for managing a specific piece of reactive state
* within a React application. This pattern promotes encapsulation and simplifies
* state management by providing a dedicated Provider, consumer hook, and HOC.
*
* The created state is instantiated once per `ReactiveProvider` and is automatically
* cleaned up, including any associated reactions, when the provider unmounts.
*
* @template SetupProps The type of props needed by the `createState` function.
* If no props are needed, this can be `undefined`.
* @template State The shape of the reactive state object. If the state object contains
* a `reactions` property (an array of `Reaction` instances), they will be automatically
* cleaned up when the provider unmounts.
* @param {CreateState<SetupProps, State>} createState A factory function that initializes
* and returns the reactive state. It is called once when the `ReactiveProvider` mounts.
* @returns {ReactiveSetup<SetupProps, State>} An object containing the setup utilities.
*/
const createReactiveSetup = createState => {
const StateContext = /*#__PURE__*/(0, _react.createContext)(null);
const ReactiveProviderCore = props => {
const state = (0, _onceRef.useOnceRef)(() => createState(props.setupProps));
(0, _react.useEffect)(() => {
return () => {
stopReactions(state.current);
};
}, []);
return /*#__PURE__*/(0, _jsxRuntime.jsx)(StateContext.Provider, {
value: state.current,
children: props.children
});
};
const ReactiveProvider = ReactiveProviderCore;
const useReactiveState = readReactiveState => {
const state = (0, _react.useContext)(StateContext);
if (state == null) {
throw new Error("useReactiveState must be used within a ReactiveProvider");
}
return (0, _hooks.useReactive)(() => readReactiveState(state), [readReactiveState]);
};
const withReactiveState = component => {
const ReactiveComponent = (0, _hoc.withReactive)(component);
const WithStateComponent = props => {
const state = (0, _react.useContext)(StateContext);
if (state == null) {
throw new Error("withReactiveState must be used within a ReactiveProvider");
}
return /*#__PURE__*/(0, _jsxRuntime.jsx)(ReactiveComponent, {
...props,
state: state
});
};
if (component.displayName || component.name) {
WithStateComponent.displayName = `withReactiveState(${component.displayName || component.name})`;
}
return WithStateComponent;
};
const MockProvider = StateContext.Provider;
return {
ReactiveProvider,
useReactiveState,
withReactiveState,
MockProvider,
createState,
StateContext: StateContext
};
};
/**
* Stops reactions provided by a state. Reactions are optional, and the convention is that, if the reactions
* are provided in an array in the "reactions" property of the returned state, they will be automatically
* cleaned up when the provided component unmounts.
*/
exports.createReactiveSetup = createReactiveSetup;
const stopReactions = currentState => {
if (currentState && typeof currentState === "object" && "reactions" in currentState && Array.isArray(currentState.reactions)) {
for (const currentReaction of currentState.reactions) {
let reaction = currentReaction;
if (reaction && typeof reaction === "object" && "stop" in reaction && typeof reaction.stop === "function") {
reaction.stop();
}
}
}
};
//# sourceMappingURL=reactive-setup.js.map