import { addCallback } from '../../lib/teact/teactn'; import { getGlobal } from '../../global'; import type { GlobalState } from '../../global/types'; import type { Signal, SignalSetter } from '../../util/signals'; import { createSignal } from '../../util/signals'; import useSyncEffect from '../useSyncEffect'; /* This hook is a more performant variation of the standard React `useSelector` hook. It allows to: a) Avoid multiple subscriptions to global updates by leveraging a single selector reference. b) Return a signal instead of forcing a component update right away. */ type Selector = (global: GlobalState) => T; interface State { clientsCount: number; getter: Signal; setter: SignalSetter; } const bySelector = new Map, State>(); addCallback((global: GlobalState) => { for (const [selector, { setter }] of bySelector) { setter(selector(global)); } }); function useSelectorSignal(selector: Selector): Signal { let state = bySelector.get(selector) as State | undefined; if (!state) { const [getter, setter] = createSignal(selector(getGlobal())); state = { clientsCount: 0, getter, setter }; bySelector.set(selector, state as State); } useSyncEffect(() => { const state2 = bySelector.get(selector)!; state2.clientsCount++; return () => { state2.clientsCount--; if (!state2.clientsCount) { bySelector.delete(selector); } }; }, [selector]); return state.getter; } export default useSelectorSignal;