@zedux/atoms
Version:
A Molecular State Engine for React
225 lines (224 loc) • 9.61 kB
TypeScript
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>
];