Hooks
No hooks found in any category.
useClosure
utilityInstallation
npx usehooks-cli@latest add use-closure
Description
A React hook that provides utilities for creating and working with JavaScript closures. Closures are functions that have access to variables from their outer (enclosing) scope even after the outer function has returned. This hook demonstrates various closure patterns and provides practical utilities for managing private state, memoization, and encapsulation.
Return Type
UseClosureReturn
Property | Type | Description |
---|---|---|
createPrivateState | <T>(initialValue: T) => PrivateState<T> | Creates a closure with private state that can only be accessed through provided methods |
createCounter | (initialValue?: number) => CounterClosure | Creates a classic counter closure with increment, decrement, and reset functionality |
createMemoizer | <T, R>(fn: (...args: T) => R) => (...args: T) => R | Creates a memoization closure that caches function results |
createModule | <T>(initialState: T, methods: Record<string, Function>) => Record<string, Function> | Creates a module pattern closure with private state and public methods |
createEventEmitter | () => EventEmitterClosure | Creates an event emitter using closures for private listener management |
createSecureState | <T>(initialValue: T, validator?: (value: T) => boolean) => SecureStateClosure<T> | Creates a secure state closure with validation and controlled access |
demonstrateScope | () => ScopeDemo | Demonstrates how closures capture and maintain access to outer scope variables |
Examples
Private State Management
Create private state that can only be modified through controlled methods
1const { createPrivateState } = useClosure();
2
3const privateCounter = createPrivateState(0);
4
5// Only way to access the value
6console.log(privateCounter.get()); // 0
7
8// Controlled modification
9privateCounter.set(prev => prev + 1);
10console.log(privateCounter.get()); // 1
11
12// Reset to initial value
13privateCounter.reset();
Classic Counter Closure
Demonstrate the classic counter closure pattern
1const { createCounter } = useClosure();
2
3const counter = createCounter(10);
4
5console.log(counter.increment()); // 11
6console.log(counter.increment()); // 12
7console.log(counter.decrement()); // 11
8console.log(counter.getValue()); // 11
9counter.reset(); // back to 10
Memoization with Closures
Create a memoized function using closures
1const { createMemoizer } = useClosure();
2
3const expensiveFunction = (n: number) => {
4 console.log('Computing...');
5 return n * n;
6};
7
8const memoized = createMemoizer(expensiveFunction);
9
10console.log(memoized(5)); // Computing... 25
11console.log(memoized(5)); // 25 (cached, no "Computing...")
12console.log(memoized(3)); // Computing... 9
Module Pattern
Create a module with private state and public methods
1const { createModule } = useClosure();
2
3const userModule = createModule(
4 { name: '', age: 0 },
5 {
6 setName: (state, name: string) => {
7 state.name = name;
8 return `Name set to ${name}`;
9 },
10 setAge: (state, age: number) => {
11 if (age >= 0) {
12 state.age = age;
13 return `Age set to ${age}`;
14 }
15 return 'Invalid age';
16 },
17 getInfo: (state) => `${state.name}, ${state.age} years old`
18 }
19);
20
21userModule.setName('John');
22userModule.setAge(30);
23console.log(userModule.getInfo()); // "John, 30 years old"
Event Emitter with Closures
Create an event emitter using closure-based private state
1const { createEventEmitter } = useClosure();
2
3const emitter = createEventEmitter();
4
5// Subscribe to events
6const unsubscribe = emitter.on('test', (data) => {
7 console.log('Received:', data);
8});
9
10// Emit events
11emitter.emit('test', 'Hello World!'); // Received: Hello World!
12
13// Unsubscribe
14unsubscribe();
Secure State with Validation
Create secure state with validation using closures
1const { createSecureState } = useClosure();
2
3const secureAge = createSecureState(
4 25,
5 (age: number) => age >= 0 && age <= 150
6);
7
8console.log(secureAge.read()); // 25
9console.log(secureAge.write(30)); // true
10console.log(secureAge.write(-5)); // false (invalid)
11console.log(secureAge.read()); // 30 (unchanged from invalid write)
Scope Demonstration
Demonstrate how closures capture outer scope variables
1const { demonstrateScope } = useClosure();
2
3const scope = demonstrateScope();
4console.log(scope.outerVar); // "I'm in the outer scope"
5
6const innerFunction = scope.createInner('inner variable');
7console.log(innerFunction());
8// "I'm in the outer scope and inner variable (from inner scope)"
Dependencies
react
Notes
- •Closures maintain access to outer scope variables even after the outer function returns
- •This hook demonstrates various closure patterns: private state, counters, memoization, modules
- •Closures are useful for data privacy, encapsulation, and creating specialized functions
- •The module pattern uses closures to create objects with private state and public methods
- •Memoization closures capture the cache in their scope for persistent storage
- •Event emitters use closures to maintain private listener collections
- •All created closures maintain their own independent scope and state
Implementation
1"use client";
2
3import { useCallback } from "react";
4
5type ClosureFunction<T = any, R = any> = (...args: T[]) => R;
6type ClosureFactory<T = any, R = any> = (
7 ...outerArgs: any[]
8) => ClosureFunction<T, R>;
9
10interface PrivateState<T> {
11 get: () => T;
12 set: (value: T | ((prev: T) => T)) => void;
13 reset: () => void;
14}
15
16interface UseClosureReturn {
17 // Create a closure with private state
18 createPrivateState: <T>(initialValue: T) => PrivateState<T>;
19
20 // Create a counter closure (classic example)
21 createCounter: (initialValue?: number) => {
22 increment: () => number;
23 decrement: () => number;
24 getValue: () => number;
25 reset: () => void;
26 };
27
28 // Create a memoization closure
29 createMemoizer: <T extends any[], R>(
30 fn: (...args: T) => R
31 ) => (...args: T) => R;
32
33 // Create a closure with multiple private variables
34 createModule: <T extends Record<string, any>>(
35 initialState: T,
36 methods: Record<string, (state: T, ...args: any[]) => any>
37 ) => Record<string, (...args: any[]) => any>;
38
39 // Create a closure-based event emitter
40 createEventEmitter: () => {
41 on: (event: string, callback: Function) => () => void;
42 emit: (event: string, ...args: any[]) => void;
43 off: (event: string, callback?: Function) => void;
44 getListeners: (event: string) => Function[];
45 };
46
47 // Create a closure with access control
48 createSecureState: <T>(
49 initialValue: T,
50 validator?: (value: T) => boolean
51 ) => {
52 read: () => T;
53 write: (value: T) => boolean;
54 isValid: (value: T) => boolean;
55 };
56
57 // Utility to demonstrate closure scope
58 demonstrateScope: () => {
59 outerVar: string;
60 createInner: (innerVar: string) => () => string;
61 };
62}
63
64export function useClosure(): UseClosureReturn {
65 const createPrivateState = useCallback(
66 <T>(initialValue: T): PrivateState<T> => {
67 let privateValue = initialValue;
68 const originalValue = initialValue;
69
70 return {
71 get: () => privateValue,
72 set: (value: T | ((prev: T) => T)) => {
73 privateValue =
74 typeof value === "function"
75 ? (value as (prev: T) => T)(privateValue)
76 : value;
77 },
78 reset: () => {
79 privateValue = originalValue;
80 },
81 };
82 },
83 []
84 );
85
86 const createCounter = useCallback((initialValue: number = 0) => {
87 let count = initialValue;
88 const initial = initialValue;
89
90 return {
91 increment: () => ++count,
92 decrement: () => --count,
93 getValue: () => count,
94 reset: () => {
95 count = initial;
96 },
97 };
98 }, []);
99
100 const createMemoizer = useCallback(
101 <T extends any[], R>(fn: (...args: T) => R) => {
102 const cache = new Map<string, R>();
103
104 return (...args: T): R => {
105 const key = JSON.stringify(args);
106
107 if (cache.has(key)) {
108 return cache.get(key)!;
109 }
110
111 const result = fn(...args);
112 cache.set(key, result);
113 return result;
114 };
115 },
116 []
117 );
118
119 const createModule = useCallback(
120 <T extends Record<string, any>>(
121 initialState: T,
122 methods: Record<string, (state: T, ...args: any[]) => any>
123 ) => {
124 let state = { ...initialState };
125 const publicMethods: Record<string, (...args: any[]) => any> = {};
126
127 // Create public methods that have access to private state
128 Object.keys(methods).forEach((methodName) => {
129 publicMethods[methodName] = (...args: any[]) => {
130 const method = methods[methodName];
131 if (typeof method === "function") {
132 return method(state, ...args);
133 }
134 throw new Error(`Method ${methodName} is not defined`);
135 };
136 });
137
138 // Add state management methods
139 publicMethods.getState = () => ({ ...state });
140 publicMethods.setState = (newState: Partial<T>) => {
141 state = { ...state, ...newState };
142 };
143
144 return publicMethods;
145 },
146 []
147 );
148
149 const createEventEmitter = useCallback(() => {
150 const listeners = new Map<string, Set<Function>>();
151
152 return {
153 on: (event: string, callback: Function) => {
154 if (!listeners.has(event)) {
155 listeners.set(event, new Set());
156 }
157 listeners.get(event)!.add(callback);
158
159 // Return unsubscribe function (closure over callback)
160 return () => {
161 listeners.get(event)?.delete(callback);
162 };
163 },
164
165 emit: (event: string, ...args: any[]) => {
166 const eventListeners = listeners.get(event);
167 if (eventListeners) {
168 eventListeners.forEach((callback) => callback(...args));
169 }
170 },
171
172 off: (event: string, callback?: Function) => {
173 if (callback) {
174 listeners.get(event)?.delete(callback);
175 } else {
176 listeners.delete(event);
177 }
178 },
179
180 getListeners: (event: string) => {
181 return Array.from(listeners.get(event) || []);
182 },
183 };
184 }, []);
185
186 const createSecureState = useCallback(
187 <T>(initialValue: T, validator?: (value: T) => boolean) => {
188 let secureValue = initialValue;
189
190 const isValid = (value: T): boolean => {
191 return validator ? validator(value) : true;
192 };
193
194 return {
195 read: () => secureValue,
196 write: (value: T): boolean => {
197 if (isValid(value)) {
198 secureValue = value;
199 return true;
200 }
201 return false;
202 },
203 isValid,
204 };
205 },
206 []
207 );
208
209 const demonstrateScope = useCallback(() => {
210 const outerVar = "I'm in the outer scope";
211
212 return {
213 outerVar,
214 createInner: (innerVar: string) => {
215 // This function closes over both outerVar and innerVar
216 return () => `${outerVar} and ${innerVar} (from inner scope)`;
217 },
218 };
219 }, []);
220
221 return {
222 createPrivateState,
223 createCounter,
224 createMemoizer,
225 createModule,
226 createEventEmitter,
227 createSecureState,
228 demonstrateScope,
229 };
230}
231