/* eslint-disable prefer-rest-params */ import { Signal, computed, react } from '@tldraw/state' import { useMemo, useSyncExternalStore } from 'react' /** * Extracts the current value from a signal and subscribes the component to changes. * * This is the most straightforward way to read signal values in React components. * When the signal changes, the component will automatically re-render with the new value. * * Note: You do not need to use this hook if you are wrapping the component with {@link track}, * as tracked components automatically subscribe to any signals accessed with `.get()`. * * @param value - The signal to read the value from * @returns The current value of the signal * * @example * ```ts * import { atom } from '@tldraw/state' * import { useValue } from '@tldraw/state-react' * * const count = atom('count', 0) * * function Counter() { * const currentCount = useValue(count) * return ( * * ) * } * ``` * * @public */ export function useValue(value: Signal): Value /** * Creates a computed value with automatic dependency tracking and subscribes to changes. * * This overload allows you to compute a value from one or more signals with automatic * memoization. The computed function will only re-execute when its dependencies change, * and the component will only re-render when the computed result changes. * * @param name - A descriptive name for debugging purposes * @param fn - Function that computes the value, should call `.get()` on any signals it depends on * @param deps - Array of signals that the computed function depends on * @returns The computed value * * @example * ```ts * import { atom } from '@tldraw/state' * import { useValue } from '@tldraw/state-react' * * const firstName = atom('firstName', 'John') * const lastName = atom('lastName', 'Doe') * * function UserGreeting() { * const fullName = useValue('fullName', () => { * return `${firstName.get()} ${lastName.get()}` * }, [firstName, lastName]) * * return
Hello {fullName}!
* } * ``` * * @public */ export function useValue(name: string, fn: () => Value, deps: unknown[]): Value /** * Implementation function for useValue hook overloads. * * Handles both single signal subscription and computed value creation with dependency tracking. * Uses React's useSyncExternalStore for efficient subscription management and automatic cleanup. * * @internal */ export function useValue() { const args = arguments // deps will be either the computed or the deps array const deps = args.length === 3 ? args[2] : [args[0]] const name = args.length === 3 ? args[0] : `useValue(${args[0].name})` const { $val, subscribe, getSnapshot } = useMemo(() => { const $val = args.length === 1 ? (args[0] as Signal) : (computed(name, args[1]) as Signal) return { $val, subscribe: (notify: () => void) => { return react(`useValue(${name})`, () => { try { $val.get() } catch { // Will be rethrown during render if the component doesn't unmount first. } notify() }) }, getSnapshot: () => $val.lastChangedEpoch, } // eslint-disable-next-line react-hooks/exhaustive-deps }, deps) useSyncExternalStore(subscribe, getSnapshot, getSnapshot) return $val.__unsafe__getWithoutCapture() }