0
# Timing & Performance
1
2
Performance optimization hooks including debouncing, throttling, intervals, timeouts, and callback optimization for efficient React applications.
3
4
## Capabilities
5
6
### useInterval
7
8
Declarative intervals with start/stop/toggle controls and automatic cleanup.
9
10
```typescript { .api }
11
/**
12
* Declarative interval with controls
13
* @param fn - Function to execute on interval
14
* @param interval - Interval duration in milliseconds
15
* @param options - Configuration for auto-invoke
16
* @returns Object with interval controls and state
17
*/
18
function useInterval(fn: () => void, interval: number, options?: UseIntervalOptions): UseIntervalReturnValue;
19
20
interface UseIntervalOptions {
21
autoInvoke?: boolean; // Start interval immediately (default: false)
22
}
23
24
interface UseIntervalReturnValue {
25
start: () => void;
26
stop: () => void;
27
toggle: () => void;
28
active: boolean;
29
}
30
```
31
32
**Usage Examples:**
33
34
```typescript
35
import { useInterval } from "@mantine/hooks";
36
37
function Timer() {
38
const [seconds, setSeconds] = useState(0);
39
40
const interval = useInterval(() => {
41
setSeconds(s => s + 1);
42
}, 1000);
43
44
return (
45
<div>
46
<p>Seconds: {seconds}</p>
47
<button onClick={interval.toggle}>
48
{interval.active ? 'Pause' : 'Start'}
49
</button>
50
<button onClick={() => setSeconds(0)}>Reset</button>
51
</div>
52
);
53
}
54
55
// Auto-start interval
56
function AutoTimer() {
57
const [count, setCount] = useState(0);
58
59
useInterval(() => {
60
setCount(c => c + 1);
61
}, 1000, { autoInvoke: true });
62
63
return <div>Count: {count}</div>;
64
}
65
```
66
67
### useTimeout
68
69
Declarative timeout with start/clear controls.
70
71
```typescript { .api }
72
/**
73
* Declarative timeout with controls
74
* @param callback - Function to execute after delay
75
* @param delay - Delay in milliseconds
76
* @param options - Configuration for auto-invoke
77
* @returns Object with timeout controls
78
*/
79
function useTimeout(callback: () => void, delay: number, options?: UseTimeoutOptions): UseTimeoutReturnValue;
80
81
interface UseTimeoutOptions {
82
autoInvoke?: boolean; // Start timeout immediately (default: false)
83
}
84
85
interface UseTimeoutReturnValue {
86
start: () => void;
87
clear: () => void;
88
}
89
```
90
91
### useDebouncedCallback
92
93
Debounced callback with cancel/flush/pending methods and advanced options.
94
95
```typescript { .api }
96
/**
97
* Debounced callback with advanced controls
98
* @param callback - Function to debounce
99
* @param delay - Debounce delay in milliseconds
100
* @param options - Debounce configuration
101
* @returns Debounced function with control methods
102
*/
103
function useDebouncedCallback<T extends (...args: any[]) => any>(
104
callback: T,
105
delay: number,
106
options?: UseDebouncedCallbackOptions
107
): UseDebouncedCallbackReturnValue<T>;
108
109
interface UseDebouncedCallbackOptions {
110
maxWait?: number; // Maximum time to wait before forcing execution
111
leading?: boolean; // Execute on leading edge
112
trailing?: boolean; // Execute on trailing edge (default: true)
113
}
114
115
type UseDebouncedCallbackReturnValue<T> = T & {
116
cancel: () => void;
117
flush: () => void;
118
pending: () => boolean;
119
};
120
```
121
122
**Usage Examples:**
123
124
```typescript
125
import { useDebouncedCallback } from "@mantine/hooks";
126
127
function SearchInput() {
128
const [query, setQuery] = useState('');
129
130
const handleSearch = useDebouncedCallback((searchQuery: string) => {
131
// API call
132
console.log('Searching for:', searchQuery);
133
}, 500, {
134
leading: false,
135
trailing: true
136
});
137
138
useEffect(() => {
139
if (query) {
140
handleSearch(query);
141
} else {
142
handleSearch.cancel(); // Cancel pending search
143
}
144
}, [query, handleSearch]);
145
146
return (
147
<div>
148
<input
149
value={query}
150
onChange={(e) => setQuery(e.target.value)}
151
placeholder="Search..."
152
/>
153
<button onClick={() => handleSearch.flush()}>
154
Search Now
155
</button>
156
<p>Pending: {handleSearch.pending() ? 'Yes' : 'No'}</p>
157
</div>
158
);
159
}
160
```
161
162
### useDebouncedState
163
164
State with debounced updates to reduce re-renders.
165
166
```typescript { .api }
167
/**
168
* State with debounced updates
169
* @param defaultValue - Initial state value
170
* @param wait - Debounce delay in milliseconds
171
* @param options - Debounce configuration
172
* @returns Tuple of debounced value and setter
173
*/
174
function useDebouncedState<T>(defaultValue: T, wait: number, options?: UseDebouncedStateOptions): UseDebouncedStateReturnValue<T>;
175
176
interface UseDebouncedStateOptions {
177
leading?: boolean; // Update on leading edge
178
}
179
180
type UseDebouncedStateReturnValue<T> = [T, React.Dispatch<React.SetStateAction<T>>];
181
```
182
183
### useDebouncedValue
184
185
Debounced value updates for derived state.
186
187
```typescript { .api }
188
/**
189
* Debounced value updates
190
* @param value - Value to debounce
191
* @param wait - Debounce delay in milliseconds
192
* @param options - Debounce configuration
193
* @returns Tuple of debounced value and cancel function
194
*/
195
function useDebouncedValue<T>(value: T, wait: number, options?: UseDebouncedValueOptions): UseDebouncedValueReturnValue<T>;
196
197
interface UseDebouncedValueOptions {
198
leading?: boolean;
199
}
200
201
type UseDebouncedValueReturnValue<T> = [T, () => void];
202
```
203
204
**Usage Examples:**
205
206
```typescript
207
import { useDebouncedValue } from "@mantine/hooks";
208
209
function ExpensiveComponent({ searchQuery }: { searchQuery: string }) {
210
const [debouncedQuery, cancel] = useDebouncedValue(searchQuery, 300);
211
212
// Only re-compute when debounced value changes
213
const expensiveResults = useMemo(() => {
214
return performExpensiveSearch(debouncedQuery);
215
}, [debouncedQuery]);
216
217
return (
218
<div>
219
<p>Searching for: {debouncedQuery}</p>
220
<button onClick={cancel}>Cancel Search</button>
221
<Results data={expensiveResults} />
222
</div>
223
);
224
}
225
```
226
227
### useThrottledCallback
228
229
Throttled callback execution to limit call frequency.
230
231
```typescript { .api }
232
/**
233
* Throttled callback execution
234
* @param callback - Function to throttle
235
* @param wait - Throttle delay in milliseconds
236
* @returns Throttled function
237
*/
238
function useThrottledCallback<T extends (...args: any[]) => any>(
239
callback: T,
240
wait: number
241
): T;
242
```
243
244
### useThrottledState
245
246
State with throttled updates to limit update frequency.
247
248
```typescript { .api }
249
/**
250
* State with throttled updates
251
* @param defaultValue - Initial state value
252
* @param wait - Throttle delay in milliseconds
253
* @returns Tuple of throttled value and setter
254
*/
255
function useThrottledState<T>(defaultValue: T, wait: number): [T, React.Dispatch<React.SetStateAction<T>>];
256
```
257
258
### useThrottledValue
259
260
Throttled value updates for performance optimization.
261
262
```typescript { .api }
263
/**
264
* Throttled value updates
265
* @param value - Value to throttle
266
* @param wait - Throttle delay in milliseconds
267
* @returns Throttled value
268
*/
269
function useThrottledValue<T>(value: T, wait: number): T;
270
```
271
272
**Usage Examples:**
273
274
```typescript
275
import { useThrottledValue, useThrottledCallback } from "@mantine/hooks";
276
277
function ScrollHandler() {
278
const [scrollY, setScrollY] = useState(0);
279
280
// Throttle scroll updates for performance
281
const throttledScrollY = useThrottledValue(scrollY, 100);
282
283
const handleScroll = useThrottledCallback(() => {
284
setScrollY(window.scrollY);
285
}, 16); // ~60fps
286
287
useEffect(() => {
288
window.addEventListener('scroll', handleScroll);
289
return () => window.removeEventListener('scroll', handleScroll);
290
}, [handleScroll]);
291
292
return <div>Scroll position: {throttledScrollY}</div>;
293
}
294
```
295
296
## Performance Patterns
297
298
### Search Optimization
299
300
```typescript
301
import { useDebouncedCallback, useDebouncedValue } from "@mantine/hooks";
302
303
function OptimizedSearch() {
304
const [query, setQuery] = useState('');
305
const [results, setResults] = useState([]);
306
const [loading, setLoading] = useState(false);
307
308
// Debounce the search function
309
const debouncedSearch = useDebouncedCallback(async (searchTerm: string) => {
310
if (!searchTerm.trim()) {
311
setResults([]);
312
return;
313
}
314
315
setLoading(true);
316
try {
317
const response = await searchAPI(searchTerm);
318
setResults(response.data);
319
} finally {
320
setLoading(false);
321
}
322
}, 300);
323
324
// Alternative: debounce the value
325
const [debouncedQuery] = useDebouncedValue(query, 300);
326
327
useEffect(() => {
328
debouncedSearch(query);
329
}, [query, debouncedSearch]);
330
331
return (
332
<div>
333
<input
334
value={query}
335
onChange={(e) => setQuery(e.target.value)}
336
placeholder="Search..."
337
/>
338
{loading && <div>Searching...</div>}
339
<SearchResults results={results} />
340
</div>
341
);
342
}
343
```
344
345
### Animation Control
346
347
```typescript
348
import { useInterval, useTimeout } from "@mantine/hooks";
349
350
function AnimationController() {
351
const [progress, setProgress] = useState(0);
352
const [isPlaying, setIsPlaying] = useState(false);
353
354
const animation = useInterval(() => {
355
setProgress(p => {
356
if (p >= 100) {
357
animation.stop();
358
setIsPlaying(false);
359
return 100;
360
}
361
return p + 1;
362
});
363
}, 50);
364
365
const resetTimeout = useTimeout(() => {
366
setProgress(0);
367
}, 1000);
368
369
const startAnimation = () => {
370
setIsPlaying(true);
371
animation.start();
372
};
373
374
const resetAnimation = () => {
375
animation.stop();
376
setIsPlaying(false);
377
resetTimeout.start();
378
};
379
380
return (
381
<div>
382
<div style={{ width: `${progress}%`, height: '20px', background: 'blue' }} />
383
<button onClick={startAnimation} disabled={isPlaying}>
384
Start
385
</button>
386
<button onClick={resetAnimation}>
387
Reset
388
</button>
389
</div>
390
);
391
}
392
```