From 2ac649cc4d85056161f06549514bf06655692061 Mon Sep 17 00:00:00 2001 From: Damian Kocjan Date: Mon, 9 Nov 2026 21:31:52 +0100 Subject: [PATCH] perf: improve useSet operations performance Instead of converting set objects to array and using array methods (O(n)), we utilize direct set methods (O(1)) --- src/useSet.ts | 43 +++++++++++++++++++++++++++++++++++-------- tests/useSet.test.ts | 4 ++++ 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/useSet.ts b/src/useSet.ts index 9c88306cc9..c0c86c3b36 100644 --- a/src/useSet.ts +++ b/src/useSet.ts @@ -16,17 +16,44 @@ 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 add = (item: K) => + setSet((prevSet) => { + if (prevSet.has(item)) return prevSet; + + const newSet = new Set(prevSet); + newSet.add(item); + return newSet; + }); + const remove = (item: K) => - setSet((prevSet) => new Set(Array.from(prevSet).filter((i) => i !== item))); + setSet((prevSet) => { + if (!prevSet.has(item)) return prevSet; + + const newSet = new Set(prevSet); + newSet.delete(item); + return newSet; + }); + const toggle = (item: K) => - setSet((prevSet) => - prevSet.has(item) - ? new Set(Array.from(prevSet).filter((i) => i !== item)) - : new Set([...Array.from(prevSet), item]) - ); + setSet((prevSet) => { + const newSet = new Set(prevSet); + if (newSet.has(item)) { + newSet.delete(item); + } else { + newSet.add(item); + } + return newSet; + }); + + const reset = () => setSet(initialSet); + + const clear = () => + setSet((prevSet) => { + if (prevSet.size === 0) return prevSet; + return new Set(); + }); - return { add, remove, toggle, reset: () => setSet(initialSet), clear: () => setSet(new Set()) }; + return { add, remove, toggle, reset, clear }; }, [setSet]); const utils = { diff --git a/tests/useSet.test.ts b/tests/useSet.test.ts index b4daa70d3e..8967fbc575 100644 --- a/tests/useSet.test.ts +++ b/tests/useSet.test.ts @@ -76,6 +76,7 @@ it('should add a new key', () => { it('should work if setting existing key', () => { const { result } = setUp(new Set(['oldKey'])); + const initialSet = result.current[0]; const [, utils] = result.current; act(() => { @@ -83,6 +84,7 @@ it('should work if setting existing key', () => { }); expect(result.current[0]).toEqual(new Set(['oldKey'])); + expect(result.current[0]).toBe(initialSet); }); it('should remove existing key', () => { @@ -120,6 +122,7 @@ it('should add a new key on toggle', () => { it('should do nothing if removing non-existing key', () => { const { result } = setUp(new Set(['a', 'b'])); + const initialSet = result.current[0]; const [, utils] = result.current; act(() => { @@ -127,6 +130,7 @@ it('should do nothing if removing non-existing key', () => { }); expect(result.current[0]).toEqual(new Set(['a', 'b'])); + expect(result.current[0]).toBe(initialSet); }); it('should reset to initial set provided', () => {