Hooks
No hooks found in any category.
useSet
stateInstallation
npx usehooks-cli@latest add use-set
Description
A React hook that provides a stateful Set with comprehensive methods for manipulation. Offers all standard Set operations plus additional utilities like filtering, set operations, and array conversion.
Parameters
Name | Type | Default | Description |
---|---|---|---|
initialValues? | T[] | Set<T> | - | Initial values for the Set (array or existing Set) |
Return Type
UseSetReturn<T>
Property | Type | Description |
---|---|---|
set | Set<T> | The current Set instance |
add | (value: T) => void | Add a single value to the Set |
addMultiple | (...values: T[]) => void | Add multiple values to the Set |
remove | (value: T) => boolean | Remove a value from the Set, returns true if removed |
removeMultiple | (...values: T[]) => void | Remove multiple values from the Set |
clear | () => void | Remove all values from the Set |
has | (value: T) => boolean | Check if the Set contains a value |
toggle | (value: T) => void | Add value if not present, remove if present |
replace | (oldValue: T, newValue: T) => boolean | Replace an old value with a new value |
filter | (predicate: (value: T) => boolean) => void | Filter the Set based on a predicate function |
union | (otherSet: Set<T> | T[]) => void | Add all values from another Set or array |
intersection | (otherSet: Set<T> | T[]) => void | Keep only values that exist in both Sets |
difference | (otherSet: Set<T> | T[]) => void | Remove values that exist in another Set |
isSubsetOf | (otherSet: Set<T> | T[]) => boolean | Check if this Set is a subset of another |
isSupersetOf | (otherSet: Set<T> | T[]) => boolean | Check if this Set is a superset of another |
toArray | () => T[] | Convert the Set to an array |
reset | () => void | Reset the Set to its initial values |
size | number | Current number of values in the Set |
isEmpty | boolean | Whether the Set is empty |
Examples
Managing a Set of tags
1import { useSet } from '@usehooks.io/use-set';
2
3function TagManager() {
4 const {
5 set: tags,
6 add,
7 remove,
8 toggle,
9 clear,
10 has,
11 size,
12 isEmpty,
13 toArray
14 } = useSet(['react', 'typescript']);
15
16 return (
17 <div>
18 <p>Tags ({size}): {isEmpty ? 'None' : toArray().join(', ')}</p>
19
20 <button onClick={() => add('javascript')}>
21 Add JavaScript
22 </button>
23
24 <button onClick={() => toggle('vue')}>
25 Toggle Vue
26 </button>
27
28 <button onClick={() => remove('react')}>
29 Remove React
30 </button>
31
32 <button onClick={clear}>
33 Clear All
34 </button>
35
36 <p>Has React: {has('react') ? 'Yes' : 'No'}</p>
37 </div>
38 );
39}
Dependencies
react
Notes
- •Optimizes re-renders by only updating state when Set actually changes
- •Supports both Set and array inputs for set operations
- •All methods are memoized with useCallback for performance
- •Maintains referential equality when possible to prevent unnecessary re-renders
Implementation
1"use client";
2
3import { useState, useCallback } from "react";
4
5interface UseSetReturn<T> {
6 set: Set<T>;
7 add: (value: T) => void;
8 addMultiple: (...values: T[]) => void;
9 remove: (value: T) => boolean;
10 removeMultiple: (...values: T[]) => void;
11 clear: () => void;
12 has: (value: T) => boolean;
13 toggle: (value: T) => void;
14 replace: (oldValue: T, newValue: T) => boolean;
15 filter: (predicate: (value: T) => boolean) => void;
16 union: (otherSet: Set<T> | T[]) => void;
17 intersection: (otherSet: Set<T> | T[]) => void;
18 difference: (otherSet: Set<T> | T[]) => void;
19 isSubsetOf: (otherSet: Set<T> | T[]) => boolean;
20 isSupersetOf: (otherSet: Set<T> | T[]) => boolean;
21 toArray: () => T[];
22 reset: () => void;
23 size: number;
24 isEmpty: boolean;
25}
26
27export function useSet<T>(initialValues?: T[] | Set<T>): UseSetReturn<T> {
28 const [set, setSet] = useState<Set<T>>(() => {
29 if (initialValues instanceof Set) {
30 return new Set(initialValues);
31 }
32 return new Set(initialValues || []);
33 });
34
35 const add = useCallback((value: T) => {
36 setSet((prev) => {
37 if (prev.has(value)) return prev;
38 const newSet = new Set(prev);
39 newSet.add(value);
40 return newSet;
41 });
42 }, []);
43
44 const addMultiple = useCallback((...values: T[]) => {
45 setSet((prev) => {
46 const newSet = new Set(prev);
47 let hasChanges = false;
48 values.forEach((value) => {
49 if (!newSet.has(value)) {
50 newSet.add(value);
51 hasChanges = true;
52 }
53 });
54 return hasChanges ? newSet : prev;
55 });
56 }, []);
57
58 const remove = useCallback((value: T): boolean => {
59 let wasRemoved = false;
60 setSet((prev) => {
61 if (!prev.has(value)) {
62 wasRemoved = false;
63 return prev;
64 }
65 const newSet = new Set(prev);
66 wasRemoved = newSet.delete(value);
67 return newSet;
68 });
69 return wasRemoved;
70 }, []);
71
72 const removeMultiple = useCallback((...values: T[]) => {
73 setSet((prev) => {
74 const newSet = new Set(prev);
75 let hasChanges = false;
76 values.forEach((value) => {
77 if (newSet.delete(value)) {
78 hasChanges = true;
79 }
80 });
81 return hasChanges ? newSet : prev;
82 });
83 }, []);
84
85 const clear = useCallback(() => {
86 setSet((prev) => (prev.size === 0 ? prev : new Set()));
87 }, []);
88
89 const has = useCallback(
90 (value: T): boolean => {
91 return set.has(value);
92 },
93 [set]
94 );
95
96 const toggle = useCallback((value: T) => {
97 setSet((prev) => {
98 const newSet = new Set(prev);
99 if (newSet.has(value)) {
100 newSet.delete(value);
101 } else {
102 newSet.add(value);
103 }
104 return newSet;
105 });
106 }, []);
107
108 const replace = useCallback((oldValue: T, newValue: T): boolean => {
109 let wasReplaced = false;
110 setSet((prev) => {
111 if (!prev.has(oldValue)) {
112 wasReplaced = false;
113 return prev;
114 }
115 const newSet = new Set(prev);
116 newSet.delete(oldValue);
117 newSet.add(newValue);
118 wasReplaced = true;
119 return newSet;
120 });
121 return wasReplaced;
122 }, []);
123
124 const filter = useCallback((predicate: (value: T) => boolean) => {
125 setSet((prev) => {
126 const newSet = new Set<T>();
127 let hasChanges = false;
128 prev.forEach((value) => {
129 if (predicate(value)) {
130 newSet.add(value);
131 } else {
132 hasChanges = true;
133 }
134 });
135 return hasChanges ? newSet : prev;
136 });
137 }, []);
138
139 const union = useCallback(
140 (otherSet: Set<T> | T[]) => {
141 const otherValues = Array.isArray(otherSet)
142 ? otherSet
143 : Array.from(otherSet);
144 addMultiple(...otherValues);
145 },
146 [addMultiple]
147 );
148
149 const intersection = useCallback((otherSet: Set<T> | T[]) => {
150 const otherSetInstance = Array.isArray(otherSet)
151 ? new Set(otherSet)
152 : otherSet;
153 setSet((prev) => {
154 const newSet = new Set<T>();
155 prev.forEach((value) => {
156 if (otherSetInstance.has(value)) {
157 newSet.add(value);
158 }
159 });
160 return newSet.size !== prev.size ? newSet : prev;
161 });
162 }, []);
163
164 const difference = useCallback((otherSet: Set<T> | T[]) => {
165 const otherSetInstance = Array.isArray(otherSet)
166 ? new Set(otherSet)
167 : otherSet;
168 setSet((prev) => {
169 const newSet = new Set<T>();
170 let hasChanges = false;
171 prev.forEach((value) => {
172 if (!otherSetInstance.has(value)) {
173 newSet.add(value);
174 } else {
175 hasChanges = true;
176 }
177 });
178 return hasChanges ? newSet : prev;
179 });
180 }, []);
181
182 const isSubsetOf = useCallback(
183 (otherSet: Set<T> | T[]): boolean => {
184 const otherSetInstance = Array.isArray(otherSet)
185 ? new Set(otherSet)
186 : otherSet;
187 for (const value of set) {
188 if (!otherSetInstance.has(value)) {
189 return false;
190 }
191 }
192 return true;
193 },
194 [set]
195 );
196
197 const isSupersetOf = useCallback(
198 (otherSet: Set<T> | T[]): boolean => {
199 const otherSetInstance = Array.isArray(otherSet)
200 ? new Set(otherSet)
201 : otherSet;
202 for (const value of otherSetInstance) {
203 if (!set.has(value)) {
204 return false;
205 }
206 }
207 return true;
208 },
209 [set]
210 );
211
212 const toArray = useCallback((): T[] => {
213 return Array.from(set);
214 }, [set]);
215
216 const reset = useCallback(() => {
217 const initialSet =
218 initialValues instanceof Set
219 ? new Set(initialValues)
220 : new Set(initialValues || []);
221 setSet(initialSet);
222 }, [initialValues]);
223
224 return {
225 set,
226 add,
227 addMultiple,
228 remove,
229 removeMultiple,
230 clear,
231 has,
232 toggle,
233 replace,
234 filter,
235 union,
236 intersection,
237 difference,
238 isSubsetOf,
239 isSupersetOf,
240 toArray,
241 reset,
242 size: set.size,
243 isEmpty: set.size === 0,
244 };
245}
246
247export default useSet;
248