UNPKG

rvx

Version:

A signal based rendering library

547 lines 16.3 kB
/** * A function that is called to dispose something. */ export type TeardownHook = () => void; /** * Run a function while capturing teardown hooks. * * + If an error is thrown by the specified function, teardown hooks are called in reverse registration order and the error is re-thrown. * + If an error is thrown by a teardown hook, remaining ones are not called and the error is re-thrown. * * @param fn The function to run. * @returns A function to run all captured teardown hooks in reverse registration order. */ export declare function capture(fn: () => void): TeardownHook; /** * Run a function while capturing teardown hooks. * * + When disposed before the specified function finishes, teardown hooks are called in reverse registration order immediately after the function finishes. * + If an error is thrown by the specified function, teardown hooks are called in reverse registration order and the error is re-thrown. * + If an error is thrown by a teardown hook, remaining ones are not called and the error is re-thrown. * * @param fn The function to run. * @returns The function's return value. */ export declare function captureSelf<T>(fn: (dispose: TeardownHook) => T): T; /** * Run a function while intentionally leaking teardown hooks. * * @param fn The function to run. * @returns The function's return value. */ export declare function leak<T>(fn: () => T): T; /** * Run a function and immediately call teardown hooks if it throws an error. * * + If an error is thrown, teardown hooks are immediately called in reverse registration order and the error is re-thrown. * + If no error is thrown, teardown hooks are registered in the outer context. * * @param fn The function to run. * @returns The function's return value. */ export declare function teardownOnError<T>(fn: () => T): T; /** * Register a teardown hook to be called when the current lifecycle is disposed. * * This has no effect if teardown hooks are not captured in the current context. * * @param hook The hook to register. This may be called multiple times. */ export declare function teardown(hook: TeardownHook): void; /** * Run a function in isolation from the following side effect causing APIs: * + Teardown hooks are leaked. To isolate only teardown hooks, use {@link leak} instead. * + Signal accesses are not tracked and the default tracking behavior is restored. To only isolate signal accesses, use {@link untrack} instead. * * Note, that batches and contexts are not isolated. * * @param fn The function to run. * @param args The function arguments. * @returns The function's return value. */ export declare function isolate<F extends (...args: any) => any>(fn: F, ...args: Parameters<F>): ReturnType<F>; /** * Internal test utility to check if lifecycle & access tracking is isolated in the current context. */ export declare function isIsolated(): boolean; /** * Represents the source that a signal has been derived from. * * When deriving a signal, the source should be passed via the signal constructor or shorthand. * This has no impact on how a signal behaves, but allows other APIs to locate metadata about a signal's source. * * @example * ```js * function trim(source: Signal<string>) { * const input = $(source.value, source); * ... * return input; * } * ``` */ export type SignalSource = Signal<unknown> | undefined; /** * Represents a value that changes over time. */ export declare class Signal<T> { #private; /** * The current value without access tracking or observer notifications when set. */ inert: T; /** * Create a new signal. * * @param value The initial value. * @param source The {@link SignalSource source} this signal has been derived from. */ constructor(value: T, source?: SignalSource); /** * Reactively access the current value. */ get value(): T; /** * Set the current value. * * If the new value is the same as the previous one, no observers are notified. * * @example * ```tsx * import { $, watch } from "rvx"; * * const count = $(0); * * watch(count, count => { * console.log("Count:", count); * }); * * count.value++; * ``` */ set value(value: T); /** * The {@link SignalSource source}, this signal has been derived from. */ get source(): SignalSource; /** * The root {@link SignalSource source}, this signal has been derived from or this signal itself if it hasn't been derived. */ get root(): Signal<unknown>; /** * Check if this signal has any active observers. */ get active(): boolean; /** * Manually access this signal. */ access(): void; /** * Manually notify observers. * * During batches, notifications are deferred. */ notify(): void; /** * Pass this signal to a function and get its result. * * @example * ```tsx * const value = $(42); * * <TextInput value={ * value * .pipe(parseInt) * .pipe(trim) * } /> * ``` */ pipe<A extends any[], R>(fn: (self: this, ...args: A) => R, ...args: A): R; } /** * Create a new signal. * * @param value The initial value. * @param source The {@link SignalSource source} this signal has been derived from. * @returns The signal. */ export declare function $(): Signal<void>; export declare function $<T>(value: T, source?: SignalSource): Signal<T>; /** * A value, signal or function to get a value. * * @example * ```tsx * import { $, watch } from "rvx"; * * const message = $("Example"); * * // Not reactive: * watch(message.value, message => { * console.log("A:", message); * }); * * // Reactive: * watch(message, message => { * console.log("B:", message); * }); * * // Reactive: * watch(() => message.value, message => { * console.log("C:", message); * }); * * message.value = "Hello World!"; * ``` */ export type Expression<T> = T | Signal<T> | (() => T); /** * Utility to get the result type of an expression. */ export type ExpressionResult<T> = T extends Expression<infer R> ? R : never; /** * Utility type for expressions that should never be static values. * * This can be used instead of the {@link Expression} type in places where accepting static values doesn't make sense. */ export type Reactive<T> = Signal<T> | (() => T); /** * Utility type to require `T` to not be reactive. */ export type Static<T> = unknown extends T ? never : Exclude<T, Reactive<any>>; /** * Watch an expression until the current lifecycle is disposed. * * + Both the expression and effect are called at least once immediately. * + Lifecycle hooks from the expression or effect are called before a signal update is processed or when the current lifecycle is disposed. * * @param expr The expression to watch. * @param effect An optional function to call with each expression result without tracking signal accesses. * * @example * ```tsx * import { $, watch } from "rvx"; * * const count = $(0); * * // Capture teardown hooks registered by "watch": * const dispose = capture(() => { * // Start watching: * watch(count, count => { * console.log("Count:", count); * }); * }); * * count.value = 1; * * // Stop watching: * dispose(); * * count.value = 2; * ``` */ export declare function watch<T>(expr: Reactive<T>, effect: (value: T) => void): void; /** * @deprecated * This call can be removed because the expression is always static. You can call the effect directly. */ export declare function watch<T>(expr: Static<T>, effect: (value: T) => void): void; /** * Watch an expression until the current lifecycle is disposed. * * + Both the expression and effect are called at least once immediately. * + Lifecycle hooks from the expression or effect are called before a signal update is processed or when the current lifecycle is disposed. * * @param expr The expression to watch. * @param effect An optional function to call with each expression result without tracking signal accesses. * * @example * ```tsx * import { $, watch } from "rvx"; * * const count = $(0); * * // Capture teardown hooks registered by "watch": * const dispose = capture(() => { * // Start watching: * watch(count, count => { * console.log("Count:", count); * }); * }); * * count.value = 1; * * // Stop watching: * dispose(); * * count.value = 2; * ``` */ export declare function watch(expr: () => void): void; export declare function watch<T>(expr: Expression<T>, effect: (value: T) => void): void; /** * Watch an expression until the current lifecycle is disposed. * * @param expr The expression to watch. * @param effect A function to call with each subsequent expression result without tracking signal accesses. * @returns The first expression result. */ export declare function watchUpdates<T>(expr: Reactive<T>, effect: (value: T) => void): T; /** * @deprecated * This call can be removed because the expression is always static. */ export declare function watchUpdates<T>(expr: Static<T>, effect: (value: T) => void): T; /** * Watch an expression until the current lifecycle is disposed. * * @param expr The expression to watch. * @param effect A function to call with each subsequent expression result without tracking signal accesses. * @returns The first expression result. */ export declare function watchUpdates<T>(expr: Expression<T>, effect: (value: T) => void): T; /** * Wrap an expression to re-run only when any accessed signal has been updated. * * + Lifecycle hooks in the expression are not supported. * + The context from where `lazy` was called is available within the expression. * * @param expr The expression to wrap. */ export declare function lazy<T>(expr: () => T): () => T; /** * Defer signal updates until a function finishes. * * + When nesting batches, updates are processed after the most outer batch has completed. * + When updates cause immediate side effects, these side effects will run as part of the batch. * * @param fn The function to run. * @returns The function's return value. * * @example * The example below outputs `5` and `9` once. Without batching the output would be `5, 7, 9`. * ```tsx * import { batch, $, watch } from "rvx"; * * const a = $(2); * const b = $(3); * * watch(() => a.value + b.value, value => { * console.log("Sum:", value); * }); * * batch(() => { * a.value = 4; * b.value = 5; * }); * ``` */ export declare function batch<T>(fn: () => T): T; /** * {@link watch Watch} an expression and get a function to reactively access its result. * * @param expr The expression to watch. * @returns A function to reactively access the latest result. * * @example * ```tsx * import { $, memo, watch } from "rvx"; * * const count = $(42); * * const computed = memo(() => someExpensiveComputation(count.value)); * * watch(computed, count => { * console.log("Count:", count); * }); * ``` */ export declare function memo<T>(expr: Reactive<T>): () => T; /** * @deprecated * This call can be removed because the expression is always static. You can use the value directly. */ export declare function memo<T>(expr: Static<T>): () => T; /** * {@link watch Watch} an expression and get a function to reactively access its result. * * @param expr The expression to watch. * @returns A function to reactively access the latest result. * * @example * ```tsx * import { $, memo, watch } from "rvx"; * * const count = $(42); * * const computed = memo(() => someExpensiveComputation(count.value)); * * watch(computed, count => { * console.log("Count:", count); * }); * ``` */ export declare function memo<T>(expr: Expression<T>): () => T; /** * {@link get Evaluate an expression} without tracking signal accesses. * * @param expr The expression to evaluate. * @returns The function's return value. * * @example * ```tsx * import { $, untrack, watch } from "rvx"; * * const a = $(2); * const b = $(3); * * watch(() => a.value + untrack(b), sum => { * console.log("Sum:", sum); * }); * * // This causes an update: * a.value = 4; * * // This has no effect: * b.value = 5; * ``` */ export declare function untrack<T>(expr: Reactive<T>): T; /** * @deprecated * This call can be removed because the expression is always static. You can use the value directly. */ export declare function untrack<T>(expr: Static<T>): T; /** * {@link get Evaluate an expression} without tracking signal accesses. * * @param expr The expression to evaluate. * @returns The function's return value. * * @example * ```tsx * import { $, untrack, watch } from "rvx"; * * const a = $(2); * const b = $(3); * * watch(() => a.value + untrack(b), sum => { * console.log("Sum:", sum); * }); * * // This causes an update: * a.value = 4; * * // This has no effect: * b.value = 5; * ``` */ export declare function untrack<T>(expr: Expression<T>): T; /** * Check if signal accesses are currently tracked. */ export declare function isTracking(): boolean; /** * Run a function while tracking signal accesses to invoke the trigger callback when updated. * * See {@link trigger}. */ export interface TriggerPipe { <T>(expr: Reactive<T>): T; /** * @deprecated * This call can be removed because the expression is always static. You can use the value directly. */ <T>(expr: Static<T>): T; <T>(expr: Expression<T>): T; } /** * Create an expression evaluator pipe that calls a function once when any accessed signals from the latest evaluated expression are updated. * * + When the lifecycle at which the pipe was created is disposed, the callback function will not be called anymore. * + It is guaranteed that the function is called before any other observers like {@link watch} or {@link effect} are notified. * + If pipes are nested, the callback for the most inner one is called first. * * @param callback The callback to invoke when a signal is updated. * @returns The pipe to evaluate expressions. */ export declare function trigger(callback: () => void): TriggerPipe; /** * Manually evaluate an expression in the current context. * * This can be used to access reactive and non reactive inputs. * * @param expr The expression to evaluate. * @returns The expression result. * * @example * ```tsx * import { $, get } from "rvx"; * * const count = $(42); * * get(42) // 42 * get(count) // 42 * get(() => 42) // 42 * ``` */ export declare function get<T>(expr: Reactive<T>): T; /** * @deprecated * This call can be removed because the expression is always static. You can use the value directly. */ export declare function get<T>(expr: Static<T>): T; /** * Manually evaluate an expression in the current context. * * This can be used to access reactive and non reactive inputs. * * @param expr The expression to evaluate. * @returns The expression result. * * @example * ```tsx * import { $, get } from "rvx"; * * const count = $(42); * * get(42) // 42 * get(count) // 42 * get(() => 42) // 42 * ``` */ export declare function get<T>(expr: Expression<T>): T; export type MapFn<I, O> = (input: I) => O; /** * Map an expression value while preserving if the expression is static or not. * * @example * ```tsx * import { $, map, get } from "rvx"; * * const count = $(42); * const doubleCount = map(count, value => value * 2); * * get(doubleCount) // 84 * ``` */ export declare function map<I, O>(input: Reactive<I>, mapFn: MapFn<I, O>): Expression<O>; /** * @deprecated * This call can be removed because the input is always static. You can call the map function directly. */ export declare function map<I, O>(input: Static<I>, mapFn: MapFn<I, O>): Expression<O>; /** * Map an expression value while preserving if the expression is static or not. * * @example * ```tsx * import { $, map, get } from "rvx"; * * const count = $(42); * const doubleCount = map(count, value => value * 2); * * get(doubleCount) // 84 * ``` */ export declare function map<I, O>(input: Expression<I>, mapFn: MapFn<I, O>): Expression<O>; //# sourceMappingURL=signals.d.ts.map