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 useEffectOnce from '../useEffectOnce'; /* 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); if (!state) { const [getter, setter] = createSignal(selector(getGlobal())); state = { clientsCount: 0, getter, setter }; bySelector.set(selector, state); } useEffectOnce(() => { state!.clientsCount++; return () => { state!.clientsCount--; if (!state!.clientsCount) { bySelector.delete(selector); } }; }); return state.getter as Signal; } export default useSelectorSignal;