fluidstate
Version:
Library for fine-grained reactivity state management
215 lines (214 loc) • 8.87 kB
TypeScript
/**
* 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;