UNPKG

@tldraw/state

Version:

tldraw infinite canvas SDK (state).

995 lines (957 loc) • 32.8 kB
/* Excluded from this release type: ArraySet */ /** * An Atom is a signal that can be updated directly by calling {@link Atom.set} or {@link Atom.update}. * * Atoms are created using the {@link atom} function. * * @example * ```ts * const name = atom('name', 'John') * * print(name.get()) // 'John' * ``` * * @public */ export declare interface Atom<Value, Diff = unknown> extends Signal<Value, Diff> { /** * Sets the value of this atom to the given value. If the value is the same as the current value, this is a no-op. * * @param value - The new value to set. * @param diff - The diff to use for the update. If not provided, the diff will be computed using {@link AtomOptions.computeDiff}. */ set(value: Value, diff?: Diff): Value; /** * Updates the value of this atom using the given updater function. If the returned value is the same as the current value, this is a no-op. * * @param updater - A function that takes the current value and returns the new value. */ update(updater: (value: Value) => Value): Value; } /** * Creates a new {@link Atom}. * * An Atom is a signal that can be updated directly by calling {@link Atom.set} or {@link Atom.update}. * * @example * ```ts * const name = atom('name', 'John') * * name.get() // 'John' * * name.set('Jane') * * name.get() // 'Jane' * ``` * * @public */ export declare function atom<Value, Diff = unknown>( /** * A name for the signal. This is used for debugging and profiling purposes, it does not need to be unique. */ name: string, /** * The initial value of the signal. */ initialValue: Value, /** * The options to configure the atom. See {@link AtomOptions}. */ options?: AtomOptions<Value, Diff>): Atom<Value, Diff>; /** * The options to configure an atom, passed into the {@link atom} function. * @public */ export declare interface AtomOptions<Value, Diff> { /** * The maximum number of diffs to keep in the history buffer. * * If you don't need to compute diffs, or if you will supply diffs manually via {@link Atom.set}, you can leave this as `undefined` and no history buffer will be created. * * If you expect the value to be part of an active effect subscription all the time, and to not change multiple times inside of a single transaction, you can set this to a relatively low number (e.g. 10). * * Otherwise, set this to a higher number based on your usage pattern and memory constraints. * */ historyLength?: number; /** * A method used to compute a diff between the atom's old and new values. If provided, it will not be used unless you also specify {@link AtomOptions.historyLength}. */ computeDiff?: ComputeDiff<Value, Diff>; /** * If provided, this will be used to compare the old and new values of the atom to determine if the value has changed. * By default, values are compared using first using strict equality (`===`), then `Object.is`, and finally any `.equals` method present in the object's prototype chain. * @param a - The old value * @param b - The new value * @returns True if the values are equal, false otherwise. */ isEqual?(a: any, b: any): boolean; } /* Excluded from this release type: Child */ /** * A computed signal created via the `computed` function or `@computed` decorator. * Computed signals derive their values from other signals and automatically update when their dependencies change. * They use lazy evaluation, only recalculating when accessed and dependencies have changed. * * @example * ```ts * const firstName = atom('firstName', 'John') * const lastName = atom('lastName', 'Doe') * const fullName = computed('fullName', () => `${firstName.get()} ${lastName.get()}`) * * console.log(fullName.get()) // "John Doe" * firstName.set('Jane') * console.log(fullName.get()) // "Jane Doe" * ``` * * @public */ export declare interface Computed<Value, Diff = unknown> extends Signal<Value, Diff> { /** * Whether this computed signal is involved in an actively-running effect graph. * Returns true if there are any reactions or other computed signals depending on this one. * @public */ readonly isActivelyListening: boolean; /* Excluded from this release type: parentSet */ /* Excluded from this release type: parents */ /* Excluded from this release type: parentEpochs */ } /** * Creates a computed signal that derives its value from other signals. * Computed signals automatically update when their dependencies change and use lazy evaluation * for optimal performance. * * @example * ```ts * const name = atom('name', 'John') * const greeting = computed('greeting', () => `Hello ${name.get()}!`) * console.log(greeting.get()) // 'Hello John!' * ``` * * `computed` may also be used as a decorator for creating computed getter methods. * * @example * ```ts * class Counter { * max = 100 * count = atom<number>(0) * * @computed getRemaining() { * return this.max - this.count.get() * } * } * ``` * * You may optionally pass in a {@link ComputedOptions} when used as a decorator: * * @example * ```ts * class Counter { * max = 100 * count = atom<number>(0) * * @computed({isEqual: (a, b) => a === b}) * getRemaining() { * return this.max - this.count.get() * } * } * ``` * * @param name - The name of the signal for debugging purposes * @param compute - The function that computes the value of the signal. Receives the previous value and last computed epoch * @param options - Optional configuration for the computed signal * @returns A new computed signal * @public */ export declare function computed<Value, Diff = unknown>(name: string, compute: (previousValue: typeof UNINITIALIZED | Value, lastComputedEpoch: number) => Value | WithDiff<Value, Diff>, options?: ComputedOptions<Value, Diff>): Computed<Value, Diff>; /** * TC39 decorator for creating computed methods in classes. * * @example * ```ts * class MyClass { * value = atom('value', 10) * * @computed * doubled() { * return this.value.get() * 2 * } * } * ``` * * @param compute - The method to be decorated * @param context - The decorator context provided by TypeScript * @returns The decorated method * @public */ export declare function computed<This extends object, Value>(compute: () => Value, context: ClassMethodDecoratorContext<This, () => Value>): () => Value; /** * Legacy TypeScript decorator for creating computed methods in classes. * * @example * ```ts * class MyClass { * value = atom('value', 10) * * @computed * doubled() { * return this.value.get() * 2 * } * } * ``` * * @param target - The class prototype * @param key - The property key * @param descriptor - The property descriptor * @returns The modified property descriptor * @public */ export declare function computed(target: any, key: string, descriptor: PropertyDescriptor): PropertyDescriptor; /** * Decorator factory for creating computed methods with options. * * @example * ```ts * class MyClass { * items = atom('items', [1, 2, 3]) * * @computed({ historyLength: 10 }) * sum() { * return this.items.get().reduce((a, b) => a + b, 0) * } * } * ``` * * @param options - Configuration options for the computed signal * @returns A decorator function that can be applied to methods * @public */ export declare function computed<Value, Diff = unknown>(options?: ComputedOptions<Value, Diff>): ((target: any, key: string, descriptor: PropertyDescriptor) => PropertyDescriptor) & (<This>(compute: () => Value, context: ClassMethodDecoratorContext<This, () => Value>) => () => Value); /** * A function type that computes the difference between two values of a signal. * * This function is used to generate incremental diffs that can be applied to * reconstruct state changes over time. It's particularly useful for features * like undo/redo, synchronization, and change tracking. * * The function should analyze the previous and current values and return a * diff object that represents the change. If the diff cannot be computed * (e.g., the values are too different or incompatible), it should return * the unique symbol RESET_VALUE to indicate that a full state reset is required. * * @param previousValue - The previous value of the signal * @param currentValue - The current value of the signal * @param lastComputedEpoch - The epoch when the previous value was set * @param currentEpoch - The epoch when the current value was set * @returns A diff object representing the change, or the unique symbol RESET_VALUE if no diff can be computed * * @example * ```ts * import { atom, RESET_VALUE } from '@tldraw/state' * * // Simple numeric diff * const numberDiff: ComputeDiff<number, number> = (prev, curr) => curr - prev * * // Array diff with reset fallback * const arrayDiff: ComputeDiff<string[], { added: string[], removed: string[] }> = (prev, curr) => { * if (prev.length > 1000 || curr.length > 1000) { * return RESET_VALUE // Too complex, force reset * } * return { * added: curr.filter(item => !prev.includes(item)), * removed: prev.filter(item => !curr.includes(item)) * } * } * * const count = atom('count', 0, { computeDiff: numberDiff }) * ``` * * @public */ export declare type ComputeDiff<Value, Diff> = (previousValue: Value, currentValue: Value, lastComputedEpoch: number, currentEpoch: number) => Diff | RESET_VALUE; /** * Options for configuring computed signals. Used when calling `computed` or using the `@computed` decorator. * * @example * ```ts * const greeting = computed('greeting', () => `Hello ${name.get()}!`, { * historyLength: 10, * isEqual: (a, b) => a === b, * computeDiff: (oldVal, newVal) => ({ type: 'change', from: oldVal, to: newVal }) * }) * ``` * * @public */ export declare interface ComputedOptions<Value, Diff> { /** * The maximum number of diffs to keep in the history buffer. * * If you don't need to compute diffs, or if you will supply diffs manually via {@link Atom.set}, you can leave this as `undefined` and no history buffer will be created. * * If you expect the value to be part of an active effect subscription all the time, and to not change multiple times inside of a single transaction, you can set this to a relatively low number (e.g. 10). * * Otherwise, set this to a higher number based on your usage pattern and memory constraints. * */ historyLength?: number; /** * A method used to compute a diff between the computed's old and new values. If provided, it will not be used unless you also specify {@link ComputedOptions.historyLength}. */ computeDiff?: ComputeDiff<Value, Diff>; /** * If provided, this will be used to compare the old and new values of the computed to determine if the value has changed. * By default, values are compared using first using strict equality (`===`), then `Object.is`, and finally any `.equals` method present in the object's prototype chain. * @param a - The old value * @param b - The new value * @returns True if the values are equal, false otherwise. */ isEqual?(a: any, b: any): boolean; } /* Excluded from this release type: deferAsyncEffects */ /** * An EffectScheduler is responsible for executing side effects in response to changes in state. * * You probably don't need to use this directly unless you're integrating this library with a framework of some kind. * * Instead, use the {@link react} and {@link reactor} functions. * * @example * ```ts * const render = new EffectScheduler('render', drawToCanvas) * * render.attach() * render.execute() * ``` * * @public */ export declare const EffectScheduler: new <Result>(name: string, runEffect: (lastReactedEpoch: number) => Result, options?: EffectSchedulerOptions) => EffectScheduler<Result>; /** @public */ export declare interface EffectScheduler<Result> { /** * Whether this scheduler is attached and actively listening to its parents. * @public */ readonly isActivelyListening: boolean; /* Excluded from this release type: lastTraversedEpoch */ /** @public */ readonly name: string; /* Excluded from this release type: __debug_ancestor_epochs__ */ /** * The number of times this effect has been scheduled. * @public */ readonly scheduleCount: number; /* Excluded from this release type: parentSet */ /* Excluded from this release type: parentEpochs */ /* Excluded from this release type: parents */ /* Excluded from this release type: maybeScheduleEffect */ /* Excluded from this release type: scheduleEffect */ /* Excluded from this release type: maybeExecute */ /** * Makes this scheduler become 'actively listening' to its parents. * If it has been executed before it will immediately become eligible to receive 'maybeScheduleEffect' calls. * If it has not executed before it will need to be manually executed once to become eligible for scheduling, i.e. by calling `EffectScheduler.execute`. * @public */ attach(): void; /** * Makes this scheduler stop 'actively listening' to its parents. * It will no longer be eligible to receive 'maybeScheduleEffect' calls until `EffectScheduler.attach` is called again. * @public */ detach(): void; /** * Executes the effect immediately and returns the result. * @returns The result of the effect. * @public */ execute(): Result; } /** @public */ export declare interface EffectSchedulerOptions { /** * scheduleEffect is a function that will be called when the effect is scheduled. * * It can be used to defer running effects until a later time, for example to batch them together with requestAnimationFrame. * * * @example * ```ts * let isRafScheduled = false * const scheduledEffects: Array<() => void> = [] * const scheduleEffect = (runEffect: () => void) => { * scheduledEffects.push(runEffect) * if (!isRafScheduled) { * isRafScheduled = true * requestAnimationFrame(() => { * isRafScheduled = false * scheduledEffects.forEach((runEffect) => runEffect()) * scheduledEffects.length = 0 * }) * } * } * const stop = react('set page title', () => { * document.title = doc.title, * }, scheduleEffect) * ``` * * @param execute - A function that will execute the effect. * @returns void */ scheduleEffect?: (execute: () => void) => void; } /** * @public */ export declare const EMPTY_ARRAY: []; /** * Retrieves the underlying computed instance for a given property created with the `computed` * decorator. * * @example * ```ts * class Counter { * max = 100 * count = atom(0) * * @computed getRemaining() { * return this.max - this.count.get() * } * } * * const c = new Counter() * const remaining = getComputedInstance(c, 'getRemaining') * remaining.get() === 100 // true * c.count.set(13) * remaining.get() === 87 // true * ``` * * @param obj - The object * @param propertyName - The property name * @public */ export declare function getComputedInstance<Obj extends object, Prop extends keyof Obj>(obj: Obj, propertyName: Prop): Computed<Obj[Prop]>; /** * Returns true if the given value is an {@link Atom}. * * @param value - The value to check * @returns True if the value is an Atom, false otherwise * @example * ```ts * const myAtom = atom('test', 42) * const notAtom = 'hello' * * console.log(isAtom(myAtom)) // true * console.log(isAtom(notAtom)) // false * ``` * @public */ export declare function isAtom(value: unknown): value is Atom<unknown>; /** * Type guard function that determines whether a value is a signal (either an Atom or Computed). * * This utility function is helpful when working with mixed data types and you need to * differentiate between regular values and reactive signal containers. It returns `true` * if the provided value is either an atomic signal created with `atom()` or a computed * signal created with `computed()`. * * @param value - The value to check, can be of any type * @returns `true` if the value is a Signal (Atom or Computed), `false` otherwise * * @example * ```ts * import { atom, computed, isSignal } from '@tldraw/state' * * const count = atom('count', 5) * const doubled = computed('doubled', () => count.get() * 2) * const regularValue = 'hello' * * console.log(isSignal(count)) // true * console.log(isSignal(doubled)) // true * console.log(isSignal(regularValue)) // false * console.log(isSignal(null)) // false * ``` * * @public */ export declare function isSignal(value: any): value is Signal<any>; /** * Call this inside a computed signal function to determine whether it is the first time the function is being called. * * Mainly useful for incremental signal computation. * * @example * ```ts * const count = atom('count', 0) * const double = computed('double', (prevValue) => { * if (isUninitialized(prevValue)) { * print('First time!') * } * return count.get() * 2 * }) * ``` * * @param value - The value to check. * @public */ export declare function isUninitialized(value: any): value is UNINITIALIZED; /** * Creates a new {@link Atom} that persists its value to localStorage. * * The atom is automatically synced with localStorage - changes to the atom are saved to localStorage, * and the initial value is read from localStorage if it exists. Returns both the atom and a cleanup * function that should be called to stop syncing when the atom is no longer needed. If you need to delete * the atom, you should do it manually after all cleanup functions have been called. * * @example * ```ts * const [theme, cleanup] = localStorageAtom('theme', 'light') * * theme.get() // 'light' or value from localStorage if it exists * * theme.set('dark') // updates atom and saves to localStorage * * // When done: * cleanup() // stops syncing to localStorage * ``` * * @param name - The localStorage key and atom name. This is used for both localStorage persistence * and debugging/profiling purposes. * @param initialValue - The initial value of the atom, used if no value exists in localStorage. * @param options - Optional atom configuration. See {@link AtomOptions}. * @returns A tuple containing the atom and a cleanup function to stop localStorage syncing. * @public */ export declare function localStorageAtom<Value, Diff = unknown>(name: string, initialValue: Value, options?: AtomOptions<Value, Diff>): [Atom<Value, Diff>, () => void]; /** * Starts a new effect scheduler, scheduling the effect immediately. * * Returns a function that can be called to stop the scheduler. * * @example * ```ts * const color = atom('color', 'red') * const stop = react('set style', () => { * divElem.style.color = color.get() * }) * color.set('blue') * // divElem.style.color === 'blue' * stop() * color.set('green') * // divElem.style.color === 'blue' * ``` * * * Also useful in React applications for running effects outside of the render cycle. * * @example * ```ts * useEffect(() => react('set style', () => { * divRef.current.style.color = color.get() * }), []) * ``` * * @public */ export declare function react(name: string, fn: (lastReactedEpoch: number) => any, options?: EffectSchedulerOptions): () => void; /** * The reactor is a user-friendly interface for starting and stopping an `EffectScheduler`. * * Calling `.start()` will attach the scheduler and execute the effect immediately the first time it is called. * * If the reactor is stopped, calling `.start()` will re-attach the scheduler but will only execute the effect if any of its parents have changed since it was stopped. * * You can create a reactor with {@link reactor}. * @public */ export declare interface Reactor<T = unknown> { /** * The underlying effect scheduler. * @public */ scheduler: EffectScheduler<T>; /** * Start the scheduler. The first time this is called the effect will be scheduled immediately. * * If the reactor is stopped, calling this will start the scheduler again but will only execute the effect if any of its parents have changed since it was stopped. * * If you need to force re-execution of the effect, pass `{ force: true }`. * @public */ start(options?: { force?: boolean; }): void; /** * Stop the scheduler. * @public */ stop(): void; } /** * Creates a {@link Reactor}, which is a thin wrapper around an `EffectScheduler`. * * @public */ export declare function reactor<Result>(name: string, fn: (lastReactedEpoch: number) => Result, options?: EffectSchedulerOptions): Reactor<Result>; /** * A unique symbol used to indicate that a signal's value should be reset or that * there is insufficient history to compute diffs between epochs. * * This value is returned by {@link Signal.getDiffSince} when the requested epoch * is too far in the past and the diff sequence cannot be reconstructed. * * @example * ```ts * import { atom, getGlobalEpoch, RESET_VALUE } from '@tldraw/state' * * const count = atom('count', 0, { historyLength: 3 }) * const oldEpoch = getGlobalEpoch() * * // Make many changes that exceed history length * count.set(1) * count.set(2) * count.set(3) * count.set(4) * * const diffs = count.getDiffSince(oldEpoch) * if (diffs === RESET_VALUE) { * console.log('Too many changes, need to reset state') * } * ``` * * @public */ export declare const RESET_VALUE: unique symbol; /** * Type representing the the unique symbol RESET_VALUE symbol, used in type annotations * to indicate when a signal value should be reset or when diff computation * cannot proceed due to insufficient history. * * @public */ export declare type RESET_VALUE = typeof RESET_VALUE; /** * A reactive value container that can change over time and track diffs between sequential values. * * Signals are the foundation of the \@tldraw/state reactive system. They automatically manage * dependencies and trigger updates when their values change. Any computed signal or effect * that reads from this signal will be automatically recomputed when the signal's value changes. * * There are two types of signal: * - **Atomic signals** - Created using `atom()`. These are mutable containers that can be * directly updated using `set()` or `update()` methods. * - **Computed signals** - Created using `computed()`. These derive their values from other * signals and are automatically recomputed when dependencies change. * * @example * ```ts * import { atom, computed } from '@tldraw/state' * * // Create an atomic signal * const count = atom('count', 0) * * // Create a computed signal that derives from the atom * const doubled = computed('doubled', () => count.get() * 2) * * console.log(doubled.get()) // 0 * count.set(5) * console.log(doubled.get()) // 10 * ``` * * @public */ export declare interface Signal<Value, Diff = unknown> { /** * A human-readable identifier for this signal, used primarily for debugging and performance profiling. * * The name is displayed in debug output from {@link whyAmIRunning} and other diagnostic tools. * It does not need to be globally unique within your application. */ name: string; /** * Gets the current value of the signal and establishes a dependency relationship. * * When called from within a computed signal or effect, this signal will be automatically * tracked as a dependency. If this signal's value changes, any dependent computations * or effects will be marked for re-execution. * * @returns The current value stored in the signal */ get(): Value; /** * The global epoch number when this signal's value last changed. * * Note that this represents when the value actually changed, not when it was last computed. * A computed signal may recalculate and produce the same value without changing its epoch. * This is used internally for dependency tracking and history management. */ lastChangedEpoch: number; /** * Gets the sequence of diffs that occurred between a specific epoch and the current state. * * This method enables incremental synchronization by providing a list of changes that * have occurred since a specific point in time. If the requested epoch is too far in * the past or the signal doesn't have enough history, it returns the unique symbol RESET_VALUE * to indicate that a full state reset is required. * * @param epoch - The epoch timestamp to get diffs since * @returns An array of diff objects representing changes since the epoch, or the unique symbol RESET_VALUE if insufficient history is available */ getDiffSince(epoch: number): Diff[] | RESET_VALUE; /** * Gets the current value of the signal without establishing a dependency relationship. * * This method bypasses the automatic dependency tracking system, making it useful for * performance-critical code paths where the overhead of dependency capture would be * problematic. Use with caution as it breaks the reactive guarantees of the system. * * **Warning**: This method should only be used when you're certain that you don't need * the calling context to react to changes in this signal. * * @param ignoreErrors - Whether to suppress errors during value retrieval (optional) * @returns The current value without establishing dependencies */ __unsafe__getWithoutCapture(ignoreErrors?: boolean): Value; /* Excluded from this release type: children */ } /** * Like {@link transaction}, but does not create a new transaction if there is already one in progress. * This is the preferred way to batch state updates when you don't need the rollback functionality. * * @example * ```ts * const count = atom('count', 0) * const doubled = atom('doubled', 0) * * react('update doubled', () => { * console.log(`Count: ${count.get()}, Doubled: ${doubled.get()}`) * }) * * // This batches both updates into a single reaction * transact(() => { * count.set(5) * doubled.set(count.get() * 2) * }) * // Logs: "Count: 5, Doubled: 10" * ``` * * @param fn - The function to run in a transaction * @returns The return value of the function * @public */ export declare function transact<T>(fn: () => T): T; /** * Batches state updates, deferring side effects until after the transaction completes. * Unlike {@link transact}, this function always creates a new transaction, allowing for nested transactions. * * @example * ```ts * const firstName = atom('firstName', 'John') * const lastName = atom('lastName', 'Doe') * * react('greet', () => { * console.log(`Hello, ${firstName.get()} ${lastName.get()}!`) * }) * * // Logs "Hello, John Doe!" * * transaction(() => { * firstName.set('Jane') * lastName.set('Smith') * }) * * // Logs "Hello, Jane Smith!" * ``` * * If the function throws, the transaction is aborted and any signals that were updated during the transaction revert to their state before the transaction began. * * @example * ```ts * const firstName = atom('firstName', 'John') * const lastName = atom('lastName', 'Doe') * * react('greet', () => { * console.log(`Hello, ${firstName.get()} ${lastName.get()}!`) * }) * * // Logs "Hello, John Doe!" * * transaction(() => { * firstName.set('Jane') * throw new Error('oops') * }) * * // Does not log * // firstName.get() === 'John' * ``` * * A `rollback` callback is passed into the function. * Calling this will prevent the transaction from committing and will revert any signals that were updated during the transaction to their state before the transaction began. * * @example * ```ts * const firstName = atom('firstName', 'John') * const lastName = atom('lastName', 'Doe') * * react('greet', () => { * console.log(`Hello, ${firstName.get()} ${lastName.get()}!`) * }) * * // Logs "Hello, John Doe!" * * transaction((rollback) => { * firstName.set('Jane') * lastName.set('Smith') * rollback() * }) * * // Does not log * // firstName.get() === 'John' * // lastName.get() === 'Doe' * ``` * * @param fn - The function to run in a transaction, called with a function to roll back the change. * @returns The return value of the function * @public */ export declare function transaction<T>(fn: (rollback: () => void) => T): T; /** * A special symbol used to indicate that a computed signal has not been initialized yet. * This is passed as the `previousValue` parameter to a computed signal function on its first run. * * @example * ```ts * const count = atom('count', 0) * const double = computed('double', (prevValue) => { * if (isUninitialized(prevValue)) { * console.log('First computation!') * } * return count.get() * 2 * }) * ``` * * @public */ export declare const UNINITIALIZED: unique symbol; /** * The type of the first value passed to a computed signal function as the 'prevValue' parameter. * This type represents the uninitialized state of a computed signal before its first calculation. * * @see {@link isUninitialized} * @public */ export declare type UNINITIALIZED = typeof UNINITIALIZED; /** * Executes the given function without capturing any parents in the current capture context. * * This is mainly useful if you want to run an effect only when certain signals change while also * dereferencing other signals which should not cause the effect to rerun on their own. * * @example * ```ts * const name = atom('name', 'Sam') * const time = atom('time', () => new Date().getTime()) * * setInterval(() => { * time.set(new Date().getTime()) * }) * * react('log name changes', () => { * print(name.get(), 'was changed at', unsafe__withoutCapture(() => time.get())) * }) * * ``` * * @public */ export declare function unsafe__withoutCapture<T>(fn: () => T): T; /** * A debugging tool that tells you why a computed signal or effect is running. * Call in the body of a computed signal or effect function. * * @example * ```ts * const name = atom('name', 'Bob') * react('greeting', () => { * whyAmIRunning() * print('Hello', name.get()) * }) * * name.set('Alice') * * // 'greeting' is running because: * // 'name' changed => 'Alice' * ``` * * @public */ export declare function whyAmIRunning(): void; /** * A singleton class used to wrap computed signal values along with their diffs. * This class is used internally by the {@link withDiff} function to provide both * the computed value and its diff to the signal system. * * @example * ```ts * const count = atom('count', 0) * const double = computed('double', (prevValue) => { * const nextValue = count.get() * 2 * if (isUninitialized(prevValue)) { * return nextValue * } * return withDiff(nextValue, nextValue - prevValue) * }) * ``` * * @public */ export declare const WithDiff: { new <Value, Diff>(value: Value, diff: Diff): { diff: Diff; value: Value; }; }; /** * Interface representing a value wrapped with its corresponding diff. * Used in incremental computation to provide both the new value and the diff from the previous value. * * @public */ export declare interface WithDiff<Value, Diff> { /** * The computed value. */ value: Value; /** * The diff between the previous and current value. */ diff: Diff; } /** * When writing incrementally-computed signals it is convenient (and usually more performant) to incrementally compute the diff too. * * You can use this function to wrap the return value of a computed signal function to indicate that the diff should be used instead of calculating a new one with {@link AtomOptions.computeDiff}. * * @example * ```ts * const count = atom('count', 0) * const double = computed('double', (prevValue) => { * const nextValue = count.get() * 2 * if (isUninitialized(prevValue)) { * return nextValue * } * return withDiff(nextValue, nextValue - prevValue) * }, { historyLength: 10 }) * ``` * * * @param value - The value. * @param diff - The diff. * @public */ export declare function withDiff<Value, Diff>(value: Value, diff: Diff): WithDiff<Value, Diff>; export { }