UNPKG

@zedux/atoms

Version:

A Molecular State Engine for React

225 lines (224 loc) 9.61 kB
import { ActionChain, Observable, Settable } from '@zedux/core'; import { AtomApi } from '../classes/AtomApi.js'; import { Ecosystem } from '../classes/Ecosystem.js'; import { SelectorCache } from '../classes/Selectors.js'; import { AnyAtomInstance, AnyAtomTemplate, AtomGenerics, AtomGenericsToAtomApiGenerics, AtomInstanceType, AtomParamsType, AtomStateType } from './atoms.js'; export * from './atoms.js'; export declare type AnyNonNullishValue = {}; export interface AtomConfig<State = any> { dehydrate?: (state: State) => any; flags?: string[]; hydrate?: (dehydratedState: unknown) => State; manualHydration?: boolean; ttl?: number; } /** * The AtomGettersBase interface. You probably won't want to use this directly. * Use AtomGetters instead. */ export interface AtomGettersBase { /** * Registers a dynamic graph edge on the resolved atom instance when called * synchronously during atom or AtomSelector evaluation. When called * asynchronously, is just an alias for `ecosystem.get` */ get<A extends AnyAtomTemplate>(template: A, params: AtomParamsType<A>): AtomStateType<A>; get<A extends AnyAtomTemplate<{ Params: []; }>>(template: A): AtomStateType<A>; get<A extends AnyAtomTemplate>(template: ParamlessTemplate<A>): AtomStateType<A>; get<I extends AnyAtomInstance>(instance: I): AtomStateType<I>; /** * Registers a static graph edge on the resolved atom instance when called * synchronously during atom or AtomSelector evaluation. When called * asynchronously, is just an alias for `ecosystem.getInstance` */ getInstance<A extends AnyAtomTemplate>(template: A, params: AtomParamsType<A>, edgeInfo?: GraphEdgeInfo): AtomInstanceType<A>; getInstance<A extends AnyAtomTemplate<{ Params: []; }>>(template: A): AtomInstanceType<A>; getInstance<A extends AnyAtomTemplate>(template: ParamlessTemplate<A>): AtomInstanceType<A>; getInstance<I extends AnyAtomInstance>(instance: I, params?: [], edgeInfo?: GraphEdgeInfo): I; /** * Runs an AtomSelector which receives its own AtomGetters object and can use * those to register its own dynamic and/or static graph edges (when called * synchronously during the AtomSelector's evaluation) * * ```ts * const mySelector = ion('mySelector', ({ select }) => { * // registers a dynamic dependency on myAtom: * const dynamicVal = select(({ get }) => get(myAtom)) * * injectEffect(() => { * // doesn't register anything: * const staticVal = select(({ get }) => get(myAtom)) * }, []) // no need to pass select as a dep; it's a stable reference * }) * ``` * * @see AtomSelector */ select<T, Args extends any[]>(selectorOrConfigOrCache: Selectable<T, Args>, ...args: Args): T; } /** * AtomGetters are used all throughout Zedux. When called synchronously during * atom or AtomSelector evaluation, they register graph edges. When called * asynchronously, they're just aliases for the corresponding ecosystem method. * * ```ts * const mySelector = ion('mySelector', ({ ecosystem, get }) => { * const dynamicVal = get(myAtom) // registers graph edge * const staticVal = ecosystem.get(myAtom) // doesn't register anything * * injectEffect(() => { * const staticVal2 = get(myAtom) // doesn't register anything * // const staticVal2 = ecosystem.get(myAtom) // same exact thing * }, []) * }) * ``` */ export interface AtomGetters extends AtomGettersBase { /** * A reference to the ecosystem of the current atom instance or AtomSelector. * * The ecosystem itself has `get`, `getInstance`, and `select` methods which * can be used instead of the other AtomGetters to prevent graph dependencies * from being registered. * * ```ts * // the current component will NOT rerender when myAtom changes: * const staticVal = useAtomSelector(({ ecosystem }) => ecosystem.get(myAtom)) * * // the current component will rerender when myAtom changes: * const dynamicVal = useAtomSelector(({ get }) => get(myAtom)) * ``` */ ecosystem: Ecosystem; } export declare type AtomInstanceTtl = number | Promise<any> | Observable<any>; export declare type AtomSelector<T = any, Args extends any[] = []> = (getters: AtomGetters, ...args: Args) => T; export interface AtomSelectorConfig<T = any, Args extends any[] = []> { argsComparator?: (newArgs: Args, oldArgs: Args) => boolean; name?: string; resultsComparator?: (newResult: T, oldResult: T) => boolean; selector: AtomSelector<T, Args>; } export declare type AtomSelectorOrConfig<T = any, Args extends any[] = []> = AtomSelector<T, Args> | AtomSelectorConfig<T, Args>; export declare type AtomStateFactory<G extends AtomGenerics> = (...params: G['Params']) => AtomApi<AtomGenericsToAtomApiGenerics<G>> | G['Store'] | G['State']; export declare type AtomTuple<A extends AnyAtomTemplate> = [A, AtomParamsType<A>]; export declare type AtomValueOrFactory<G extends AtomGenerics> = AtomStateFactory<G> | G['Store'] | G['State']; export declare type Cleanup = () => void; export declare type DependentCallback = (signal: GraphEdgeSignal, val?: any, reason?: EvaluationReason) => any; export interface DependentEdge { callback?: DependentCallback; createdAt: number; dependentKey?: string; flags: number; isMaterialized?: boolean; operation: string; prevEdge?: WeakRef<DependentEdge>; task?: () => void; } export interface EcosystemConfig<Context extends Record<string, any> | undefined = any> { atomDefaults?: { ttl?: number; }; complexParams?: boolean; context?: Context; destroyOnUnmount?: boolean; flags?: string[]; id?: string; onReady?: (ecosystem: Ecosystem<Context>, prevContext?: Context) => MaybeCleanup; overrides?: AnyAtomTemplate[]; ssr?: boolean; } export interface EcosystemGraphNode { dependencies: Map<string, true>; dependents: Map<string, DependentEdge>; isSelector?: boolean; refCount: number; weight: number; } export declare type EffectCallback = () => MaybeCleanup | Promise<any>; export interface EvaluationReason<State = any> { action?: ActionChain; newState?: State; oldState?: State; operation: string; sourceType: EvaluationSourceType; sourceId?: string; reasons?: EvaluationReason[]; type: EvaluationType; } export declare type EvaluationSourceType = 'Atom' | 'AtomSelector' | 'External' | 'Injector' | 'Store'; export declare type EvaluationType = 'cache invalidated' | 'node destroyed' | 'promise changed' | 'state changed'; export declare type ExportsInfusedSetter<State, Exports> = Exports & { (settable: Settable<State>, meta?: any): State; }; export declare type GraphEdgeInfo = [ flags: number, operation: string ]; /** * A low-level detail that tells dependents what sort of event is causing the * current update. Promise changes and state updates are lumped together as * 'Update' signals. If you need to distinguish between them, look at the * EvaluationType (the `type` field) in the full reasons list. */ export declare type GraphEdgeSignal = 'Destroyed' | 'Updated'; export interface GraphViewRecursive { [key: string]: GraphViewRecursive; } export interface InjectAtomInstanceConfig { operation?: string; subscribe?: boolean; } export declare type InjectorDeps = any[] | undefined; export declare type InjectOrUseSelector<State, Params extends any[]> = Params extends [] ? <D = any>(selector: (state: State) => D) => D : <D = any>(params: Params, selector: (state: State) => D) => D; export interface InjectPromiseConfig<T = any> { dataOnly?: boolean; initialState?: T; runOnInvalidate?: boolean; } export interface InjectStoreConfig { hydrate?: boolean; subscribe?: boolean; } export declare type IonStateFactory<G extends AtomGenerics> = (getters: AtomGetters, ...params: G['Params']) => AtomApi<AtomGenericsToAtomApiGenerics<G>> | G['Store'] | G['State']; export declare type LifecycleStatus = 'Active' | 'Destroyed' | 'Initializing' | 'Stale'; export declare type MaybeCleanup = Cleanup | void; export interface MutableRefObject<T = any> { current: T; } /** * Many Zedux APIs make the `params` parameter optional if the atom doesn't take * params or has only optional params. */ export declare type ParamlessTemplate<A extends AnyAtomTemplate> = AtomParamsType<A> extends [AnyNonNullishValue | undefined | null, ...any[]] ? never : A; /** * Part of the atom instance can be accessed during initial evaluation. The only * fields that are inaccessible are those that don't exist yet 'cause the * initial evaluation is supposed to create them. */ export declare type PartialAtomInstance = Omit<AnyAtomInstance, 'api' | 'exports' | 'promise' | 'store'>; export declare type Prettify<T> = { [K in keyof T]: T[K]; } & AnyNonNullishValue; export interface PromiseState<T> { data?: T; error?: Error; isError: boolean; isLoading: boolean; isSuccess: boolean; status: PromiseStatus; } export declare type PromiseStatus = 'error' | 'loading' | 'success'; export declare type Ref<T = any> = MutableRefObject<T>; export interface RefObject<T = any> { readonly current: T | null; } export declare type Selectable<T = any, Args extends any[] = []> = AtomSelector<T, Args> | AtomSelectorConfig<T, Args> | SelectorCache<T, Args>; export declare type StateHookTuple<State, Exports> = [ State, ExportsInfusedSetter<State, Exports> ];