import { useRef } from '../lib/teact/teact'; import type { Signal } from '../util/signals'; import useForceUpdate from './useForceUpdate'; import useSyncEffect from './useSyncEffect'; import { useStateRef } from './useStateRef'; import { useSignalEffect } from './useSignalEffect'; type SyncResolver = () => T; type AsyncResolver = (setter: (newValue: T) => void) => void; type Resolver = SyncResolver | AsyncResolver; function useDerivedState(resolver: SyncResolver, dependencies: readonly any[]): T; function useDerivedState(resolver: AsyncResolver, dependencies: readonly any[], isAsync: true): T; function useDerivedState(signal: Signal): T; function useDerivedState(resolverOrSignal: Resolver | T, dependencies?: readonly any[], isAsync = false) { const resolver = dependencies ? resolverOrSignal as Resolver : () => ((resolverOrSignal as Signal)()); dependencies ??= [resolverOrSignal]; const valueRef = useRef(); const forceUpdate = useForceUpdate(); const resolverRef = useStateRef(resolver); function runCurrentResolver(isSync = false) { const currentResolver = resolverRef.current; if (isAsync) { (currentResolver as AsyncResolver)((newValue) => { if (valueRef.current !== newValue) { valueRef.current = newValue; forceUpdate(); } }); } else { const newValue = (currentResolver as SyncResolver)(); if (valueRef.current !== newValue) { valueRef.current = newValue; if (!isSync) { forceUpdate(); } } } } useSyncEffect(() => { runCurrentResolver(true); // eslint-disable-next-line react-hooks/exhaustive-deps }, dependencies); // eslint-disable-next-line react-hooks/exhaustive-deps useSignalEffect(runCurrentResolver, dependencies); return valueRef.current as T; } export default useDerivedState;