UNPKG

fluidstate

Version:

Library for fine-grained reactivity state management

215 lines (214 loc) 8.87 kB
/** * A function that customizes equality check between a previous and new value */ export type IsEqual = (oldValue: unknown, newValue: unknown) => boolean; /** * Atom is a stripped down version of a reactive signal. It is not * associated with any value - instead, it may be reported to be observed (i.e. read), * hence adding itself to the computed atoms and reactions as a dependency, or * or reported to be changed, thereby potentially triggering recalculation of * computed atoms and reactions that depend on it */ export type Atom = { reportObserved: () => boolean; reportChanged: () => void; }; /** * Options for configuring the behavior of an {@link Atom}. * This includes listeners for when the atom becomes observed or unobserved. */ export type AtomOptions = { /** * A listener callback that gets called when the atom becomes observed, i.e. when * it is first read by a reaction or by a computed atom that is obsserved by at least * one reaction */ onBecomeObservedListener?: () => void; /** * A listener callback that gets called when the atom stops being observed, i.e. when * all reactions that depend on it were stopped, or if none of those reaction * depend on it anymore */ onBecomeUnobservedListener?: () => void; }; /** * Represents a reactive value derived from other reactive sources. * Its value is cached and recomputed only when its dependencies change, * provided it's actively observed by a reaction. */ export type ComputedAtom<T> = { /** * A function that either returns a cached computed reactive atom value or * calculates the value of the atom and caches it until its dependencies change. * If the computed atom does not have any reactions that ultimately depend on it, * does not behave reactively like that - instead, it behaves like a getter that * always calculates the value upon getting it */ get: () => T; }; /** * Options for configuring reactive values, primarily concerning equality checks. */ export type ReactiveValueOptions = { equals?: IsEqual; }; /** * Options for configuring computed values, primarily concerning equality checks. */ export type ComputedOptions = { equals?: IsEqual; }; /** * Options for configuring a {@link ComputedAtom}. * Combines {@link ComputedOptions} for value comparison and {@link AtomOptions} for lifecycle events. */ export type ComputedAtomOptions = ComputedOptions & AtomOptions; export type CreateAtom = ( /** * Name of the atom that may be useful for debugging */ name: string, options?: AtomOptions) => Atom; /** * Defines the signature for a function that creates a {@link ComputedAtom}. * @template T The type of the value held by the computed atom. */ export type CreateComputedAtom = <T>( /** * Name of the atom that may be useful for debugging */ name: string, /** * A function used to calculate the atom value */ calculate: () => T, options?: ComputedAtomOptions) => ComputedAtom<T>; /** * Options for configuring a {@link Reaction}. * This primarily allows specifying a custom scheduler for reaction execution. */ export type ReactionOptions = { /** * If scheduler is provided, the reaction will not run or rerun * immediately, it will be scheduled using the provided `scheduler` * function */ scheduler?: (callback: () => void) => void; }; /** * Represents an active reaction that tracks dependencies and performs side effects. * It provides a method to stop the reaction. */ export type Reaction = { /** * A function that may be called to stop the reaction, preventing it from * re-running and cleaning up its resources. */ stop: () => void; }; /** * Defines the signature for a function that creates a {@link Reaction}. * A reaction tracks dependencies and re-runs an effect function when they change. */ export type CreateReaction = ( /** * A function that will be called or scheduled immediately, and may * track atoms and computed atoms, and may perform side-effects. This * function will be automatically called or scheduled again when those * atoms and computed atoms change */ effect: () => void, options?: ReactionOptions) => Reaction; /** * Represents the core API of a reactive system instance. * It provides methods for creating atoms, computed atoms, reactions, * and managing tracking and transactions. */ export type ReactiveInstance = { /** * Creates a reactive atom, i.e. a stripped down version of a reactive * signal. It does not concern itself with storing any data - it simply * can be reported to be observed and reported to be changed. The * association of data with the atom is the responsibility of the user */ createAtom: CreateAtom; /** * Creates a computed reactive atom. Tracks accesses of other atoms and * computed atoms during the calculation and becomes dependent on them. * The calculation is cached and will only be recalculated when its * dependencies change. It only behaves reactively like this when there * is at least one reaction observing it - otherwise it behaves like a * getter: doesn't cache and always recalculates upon being read */ createComputedAtom: CreateComputedAtom; /** * Creates a reaction that, similar to computed atoms, tracks accesses to * other atoms and computed atoms inside (unless those atoms are read inside * an `untrack` call). This reaction immediately runs or gets scheduled (if * `scheduler` is provided) and will rerun if its dependencies change. Reaction * is what ultimately activates reactivity of atoms and computed atoms. This * reactivity will be active until the reaction is stopped by calling * `reaction.stop()` */ createReaction: CreateReaction; /** * Returns whether or not the reactive library is currently * tracking a derivation (e.g. during computed atom calculation * or the tracked part of a reaction) */ isTracking: () => boolean; /** * Runs provided `action` but suspends derivation tracking while * the `action` runs */ untrack: <T>(action: () => T) => T; /** * Runs provided `transaction` that may mutate multiple reactive atoms. * Reactions are not triggered until the end of the transaction. This * is a lower level API than `runAction` */ runTransaction: <T>(transaction: () => T) => T; /** * Similar to `runTransaction` - runs provided `action` that may * mutate multiple reactive atoms, as one transaction. The difference * between `runTransaction` and `runAction` is that all reactive * atom mutations must run inside `runAction`, otherwise a * warning may be shown in console. So `runTransaction` must * generally also be called inside `runAction`. It is useful to * have a `runTransaction` as a separate function because we may * want to create functions that will internally call `runTransaction` * but will still require the user to call those functions inside * `runAction` */ runAction: <T>(action: () => T) => T; }; /** * Represents the core API of a reactive system that can be plugged into fluidstate. * `fluidstate` implements its own derivation tracking (for reactions and computed atoms) * and therefore does not require an `isTracking` method from the underlying layer. */ export type ReactiveLayer = Omit<ReactiveInstance, "isTracking">; export type ReactiveRemoteOptions = { /** * A scheduler function that controls the timing of reactions from the * remote reactive system when they depend on data from the local system. * * When a reaction in the remote system observes data in the local system, * this scheduler (if provided) will be used to enqueue the execution * of that remote reaction. This allows the local system to dictate * when updates from the remote system are processed, crucial for environments * like game engines or UI frameworks that have specific update cycles (e.g., * end of frame, next animation tick). * * The `callback` parameter is the function that, when executed, will run * the remote reaction's effect. * * If not provided, remote reactions may run according to their own system's * default scheduling or immediately upon dependency change. */ scheduler?: (callback: () => void) => void; }; /** * Represents a function to be executed as part of a reaction's cleanup process. * Cleanup functions are typically registered using `createCleanup` from within * a reaction's effect. They are called automatically when the reaction is stopped * or before it re-runs, allowing for resource management and teardown logic. */ export type ReactionCleanup = () => void;