0
# React Hooks
1
2
Modern React hooks for state management, memoization, and lifecycle optimization with enhanced functionality beyond standard React hooks.
3
4
## Capabilities
5
6
### useMergedState (useControlledState)
7
8
Hook for managing controlled/uncontrolled state patterns, commonly used in form components that can be either controlled or uncontrolled.
9
10
```typescript { .api }
11
/**
12
* Hook for controlled/uncontrolled state management
13
* @param {T | (() => T)} defaultStateValue - Default state value or function
14
* @param {object} [option] - Configuration options
15
* @returns {[R, (value: T) => void]} State value and setter function
16
*/
17
function useControlledState<T, R = T>(
18
defaultStateValue: T | (() => T),
19
option?: {
20
/** Default value when component is uncontrolled */
21
defaultValue?: T | (() => T);
22
/** External value when component is controlled */
23
value?: T;
24
/** Callback when value changes */
25
onChange?: (value: T, prevValue: T) => void;
26
/** Transform state value before returning */
27
postState?: (value: T) => T;
28
}
29
): [R, (value: T) => void];
30
```
31
32
**Usage Examples:**
33
34
```typescript
35
import useControlledState from 'rc-util/lib/hooks/useMergedState';
36
37
// Basic controlled/uncontrolled input
38
function Input({ value, defaultValue, onChange }) {
39
const [inputValue, setInputValue] = useControlledState('', {
40
value,
41
defaultValue,
42
onChange
43
});
44
45
return (
46
<input
47
value={inputValue}
48
onChange={(e) => setInputValue(e.target.value)}
49
/>
50
);
51
}
52
53
// With post-processing
54
function NumberInput({ value, onChange }) {
55
const [numValue, setNumValue] = useControlledState(0, {
56
value,
57
onChange,
58
postState: (val) => Math.max(0, val) // Ensure non-negative
59
});
60
61
return (
62
<input
63
type="number"
64
value={numValue}
65
onChange={(e) => setNumValue(Number(e.target.value))}
66
/>
67
);
68
}
69
70
// Uncontrolled with default
71
<Input defaultValue="Hello" />
72
73
// Controlled
74
<Input value={inputValue} onChange={setInputValue} />
75
```
76
77
### useEffect
78
79
Enhanced useEffect that passes previous dependencies to the callback, useful for comparison logic.
80
81
```typescript { .api }
82
/**
83
* Enhanced useEffect that passes previous dependencies to callback
84
* @param {(prevDeps: any[]) => void} callback - Effect callback receiving previous deps
85
* @param {any[]} deps - Dependencies array
86
*/
87
function useEffect(
88
callback: (prevDeps: any[]) => void,
89
deps: any[]
90
): void;
91
```
92
93
**Usage Example:**
94
95
```typescript
96
import useEffect from 'rc-util/lib/hooks/useEffect';
97
98
function Component({ userId, filterType }) {
99
useEffect((prevDeps) => {
100
const [prevUserId, prevFilterType] = prevDeps;
101
102
// Only refetch if userId changed, not filterType
103
if (prevUserId !== userId) {
104
fetchUserData(userId);
105
}
106
107
// Only update filter if filterType changed
108
if (prevFilterType !== filterType) {
109
updateFilter(filterType);
110
}
111
}, [userId, filterType]);
112
}
113
```
114
115
### useMemo
116
117
Custom memoization hook with custom comparison function for more control over when values are recalculated.
118
119
```typescript { .api }
120
/**
121
* Custom memoization hook with custom comparison function
122
* @param {() => Value} getValue - Function to get the memoized value
123
* @param {Condition} condition - Current condition for comparison
124
* @param {(prev: Condition, next: Condition) => boolean} shouldUpdate - Comparison function
125
* @returns {Value} Memoized value
126
*/
127
function useMemo<Value, Condition = any[]>(
128
getValue: () => Value,
129
condition: Condition,
130
shouldUpdate: (prev: Condition, next: Condition) => boolean
131
): Value;
132
```
133
134
**Usage Examples:**
135
136
```typescript
137
import useMemo from 'rc-util/lib/hooks/useMemo';
138
139
// Deep comparison for objects
140
function ExpensiveComponent({ config }) {
141
const processedData = useMemo(
142
() => expensiveCalculation(config),
143
config,
144
(prev, next) => JSON.stringify(prev) !== JSON.stringify(next)
145
);
146
147
return <div>{processedData}</div>;
148
}
149
150
// Custom comparison for arrays
151
function ListComponent({ items, sortBy }) {
152
const sortedItems = useMemo(
153
() => [...items].sort((a, b) => a[sortBy] - b[sortBy]),
154
{ items, sortBy },
155
(prev, next) =>
156
prev.items.length !== next.items.length ||
157
prev.sortBy !== next.sortBy ||
158
prev.items.some((item, index) => item.id !== next.items[index]?.id)
159
);
160
161
return (
162
<ul>
163
{sortedItems.map(item => <li key={item.id}>{item.name}</li>)}
164
</ul>
165
);
166
}
167
```
168
169
## Reference Utilities
170
171
### ref
172
173
React ref manipulation utilities for working with refs in complex scenarios.
174
175
```typescript { .api }
176
/**
177
* Fill a ref with a node value
178
* @param {React.Ref<T>} ref - Ref to fill (function or object ref)
179
* @param {T} node - Node value to assign
180
*/
181
function fillRef<T>(ref: React.Ref<T>, node: T): void;
182
183
/**
184
* Merge multiple refs into one ref function
185
* @param {...React.Ref<T>[]} refs - Refs to merge
186
* @returns {React.Ref<T>} Combined ref function
187
*/
188
function composeRef<T>(...refs: React.Ref<T>[]): React.Ref<T>;
189
190
/**
191
* Check if a component supports refs
192
* @param {any} nodeOrComponent - React component or node
193
* @returns {boolean} True if component supports refs
194
*/
195
function supportRef(nodeOrComponent: any): boolean;
196
```
197
198
**Usage Examples:**
199
200
```typescript
201
import { fillRef, composeRef, supportRef } from 'rc-util/lib/ref';
202
203
// Compose multiple refs
204
function ForwardedComponent(props, ref) {
205
const internalRef = useRef();
206
const combinedRef = composeRef(ref, internalRef);
207
208
useEffect(() => {
209
// Use internal ref for component logic
210
if (internalRef.current) {
211
internalRef.current.focus();
212
}
213
}, []);
214
215
return <input ref={combinedRef} />;
216
}
217
218
// Check ref support before using
219
function ParentComponent({ children }) {
220
const childRef = useRef();
221
222
const enhancedChildren = React.Children.map(children, (child) => {
223
if (supportRef(child)) {
224
return React.cloneElement(child, { ref: childRef });
225
}
226
return child;
227
});
228
229
return <div>{enhancedChildren}</div>;
230
}
231
232
// Manual ref filling
233
function CustomRef() {
234
const handleRef = useCallback((node) => {
235
// Fill multiple refs manually
236
fillRef(externalRef, node);
237
fillRef(internalRef, node);
238
}, []);
239
240
return <div ref={handleRef} />;
241
}
242
```
243
244
## Animation & Performance
245
246
### raf
247
248
RequestAnimationFrame wrapper with fallback for server-side rendering and older browsers.
249
250
```typescript { .api }
251
/**
252
* RequestAnimationFrame wrapper with fallback
253
* @param {() => void} callback - Function to call on next frame
254
* @returns {number} Request ID for cancellation
255
*/
256
function wrapperRaf(callback: () => void): number;
257
258
/**
259
* Cancel a requestAnimationFrame request
260
* @param {number} id - Request ID to cancel
261
*/
262
wrapperRaf.cancel: (id: number) => void;
263
```
264
265
**Usage Example:**
266
267
```typescript
268
import raf from 'rc-util/lib/raf';
269
270
// Schedule animation
271
const rafId = raf(() => {
272
// Animation code here
273
element.style.opacity = '1';
274
});
275
276
// Cancel if needed
277
raf.cancel(rafId);
278
279
// Use in custom hook
280
function useAnimationFrame(callback) {
281
const requestRef = useRef();
282
283
useEffect(() => {
284
const animate = () => {
285
callback();
286
requestRef.current = raf(animate);
287
};
288
requestRef.current = raf(animate);
289
290
return () => {
291
if (requestRef.current) {
292
raf.cancel(requestRef.current);
293
}
294
};
295
}, [callback]);
296
}
297
```