diff --git a/src/useMap.ts b/src/useMap.ts index ded74ed239..d768318657 100644 --- a/src/useMap.ts +++ b/src/useMap.ts @@ -1,4 +1,4 @@ -import { useCallback, useMemo, useState } from 'react'; +import { useCallback, useMemo, useReducer, useRef } from 'react'; export interface StableActions { set: (key: K, value: T[K]) => void; @@ -11,37 +11,43 @@ export interface Actions extends StableActions { get: (key: K) => T[K]; } -const useMap = (initialMap: T = {} as T): [T, Actions] => { - const [map, set] = useState(initialMap); - - const stableActions = useMemo>( - () => ({ - set: (key, entry) => { - set((prevMap) => ({ - ...prevMap, - [key]: entry, - })); - }, - setAll: (newMap: T) => { - set(newMap); - }, - remove: (key) => { - set((prevMap) => { - const { [key]: omit, ...rest } = prevMap; - return rest as T; - }); - }, - reset: () => set(initialMap), - }), - [set] - ); +const useMap = = Record>( + initialMap: T = {} as T +): [T, Actions] => { + const initialRef = useRef({ ...(initialMap as any) }); + const ref = useRef({ ...(initialMap as any) }); + const [, force] = useReducer((c: number) => c + 1, 0); + + const setKey = useCallback((key: K, value: T[K]) => { + (ref.current as any)[key] = value; + force(); + }, []); + + const setAll = useCallback((newMap: T) => { + ref.current = { ...(newMap as any) }; + force(); + }, []); + + const remove = useCallback((key: K) => { + if (key in ref.current) { + delete (ref.current as any)[key]; + force(); + } + }, []); - const utils = { - get: useCallback((key) => map[key], [map]), - ...stableActions, - } as Actions; + const reset = useCallback(() => { + ref.current = { ...(initialRef.current as any) }; + force(); + }, []); + + const get = useCallback((key: K): T[K] => ref.current[key], []); + + const utils = useMemo>( + () => ({ set: setKey, setAll, remove, reset, get }), + [setKey, setAll, remove, reset, get] + ); - return [map, utils]; + return [ref.current, utils]; }; export default useMap; diff --git a/src/useSet.ts b/src/useSet.ts index 9c88306cc9..e20286f838 100644 --- a/src/useSet.ts +++ b/src/useSet.ts @@ -1,4 +1,4 @@ -import { useCallback, useMemo, useState } from 'react'; +import { useCallback, useMemo, useReducer, useRef } from 'react'; export interface StableActions { add: (key: K) => void; @@ -12,29 +12,50 @@ export interface Actions extends StableActions { has: (key: K) => boolean; } -const useSet = (initialSet = new Set()): [Set, Actions] => { - const [set, setSet] = useState(initialSet); - - const stableActions = useMemo>(() => { - const add = (item: K) => setSet((prevSet) => new Set([...Array.from(prevSet), item])); - const remove = (item: K) => - setSet((prevSet) => new Set(Array.from(prevSet).filter((i) => i !== item))); - const toggle = (item: K) => - setSet((prevSet) => - prevSet.has(item) - ? new Set(Array.from(prevSet).filter((i) => i !== item)) - : new Set([...Array.from(prevSet), item]) - ); - - return { add, remove, toggle, reset: () => setSet(initialSet), clear: () => setSet(new Set()) }; - }, [setSet]); - - const utils = { - has: useCallback((item) => set.has(item), [set]), - ...stableActions, - } as Actions; - - return [set, utils]; +const useSet = (initial: Iterable = []): [Set, Actions] => { + const initialSet = useMemo(() => new Set(initial), []); + const ref = useRef>(new Set(initialSet)); + const [, force] = useReducer((c: number) => c + 1, 0); + + const add = useCallback((item: K) => { + if (!ref.current.has(item)) { + ref.current.add(item); + force(); + } + }, []); + + const remove = useCallback((item: K) => { + if (ref.current.delete(item)) { + force(); + } + }, []); + + const toggle = useCallback((item: K) => { + if (ref.current.has(item)) ref.current.delete(item); + else ref.current.add(item); + force(); + }, []); + + const reset = useCallback(() => { + ref.current = new Set(initialSet); + force(); + }, [initialSet]); + + const clear = useCallback(() => { + if (ref.current.size) { + ref.current.clear(); + force(); + } + }, []); + + const has = useCallback((item: K) => ref.current.has(item), []); + + const utils = useMemo>( + () => ({ add, remove, toggle, reset, clear, has }), + [add, remove, toggle, reset, clear, has] + ); + + return [ref.current, utils]; }; export default useSet;