import type { GlobalState } from '../global/types'; import type { Signal, SignalSetter } from '../util/signals'; import { getGlobal } from '../global'; import { createSignal } from '../util/signals'; import useEffectOnce from './useEffectOnce'; import { addCallback } from '../lib/teact/teactn'; /* 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; } export default useSelectorSignal;