UNPKG

@gravity-ui/graph

Version:

Modern graph editor component

68 lines (67 loc) 2.11 kB
import { useCallback, useEffect, useLayoutEffect, useMemo, useSyncExternalStore } from "react"; import { computed, effect } from "@preact/signals-core"; import { useFn } from "../utils/hooks/useFn"; /** * Hook to subscribe to a signal and get the current value. * @param signal - Signal to subscribe to * @returns Current value of the signal * * @example * ```tsx * const geometry = useSignal(block.$geometry); * ``` */ export function useSignal(signal) { const subscribe = useCallback((onChangeFn) => { return signal.subscribe(onChangeFn); }, [signal]); const getSnapshot = useCallback(() => { return signal.value; }, [signal]); return useSyncExternalStore(subscribe, getSnapshot, getSnapshot); } /** * Hook to subscribe to a computed signal and get the current value. * @param compute - Computed function to subscribe to * @param deps - Dependencies to subscribe to * @returns Current value of the computed signal * * @example * ```tsx * const geometry = useComputedSignal(() => block.$geometry.value, [block]); * ``` */ export function useComputedSignal(compute, deps) { const handle = useFn(compute); const signal = useMemo(() => computed(() => handle()), deps); return useSignal(signal); } /** * Hook to subscribe to a signal and get the current value. * @param effectFn - Effect function to subscribe to * @param deps - Dependencies recreate the effect when they change * @returns Current value of the signal * * @example * ```tsx * useSignalEffect(() => { * console.log(block.$geometry.value); * }, [block]); * ``` */ export function useSignalEffect(effectFn, deps) { const handle = useFn(effectFn); useEffect(() => { return effect(() => handle()); }, deps); } /** * Like {@link useSignalEffect}, but runs the subscription setup in `useLayoutEffect` * so the effect fires synchronously after DOM updates and before paint. */ export function useSignalLayoutEffect(effectFn, deps) { const handle = useFn(effectFn); useLayoutEffect(() => { return effect(() => handle()); }, deps); }