npm-react

Description
React is a JavaScript library for building user interfaces with a declarative, component-based approach.
Author
tessl
Last updated

How to use

npx @tessl/cli registry install tessl/npm-react@19.1.0

concurrency.md docs/

1
# Concurrency and Transitions
2
3
Concurrent features for managing non-urgent updates and improving user experience. These features help React prioritize important updates while deferring less critical ones.
4
5
## Capabilities
6
7
### useTransition
8
9
Hook for marking updates as non-urgent transitions to keep the UI responsive.
10
11
```typescript { .api }
12
/**
13
* Marks state updates as non-urgent transitions
14
* @returns Array with isPending boolean and startTransition function
15
*/
16
function useTransition(): [
17
isPending: boolean,
18
startTransition: (callback: () => void) => void
19
];
20
```
21
22
**Usage Examples:**
23
24
```typescript
25
import React, { useState, useTransition } from "react";
26
27
function SearchResults() {
28
const [query, setQuery] = useState("");
29
const [results, setResults] = useState<string[]>([]);
30
const [isPending, startTransition] = useTransition();
31
32
const handleSearch = (newQuery: string) => {
33
// Update input immediately (urgent)
34
setQuery(newQuery);
35
36
// Update results as transition (non-urgent)
37
startTransition(() => {
38
// Simulate expensive search operation
39
const searchResults = performExpensiveSearch(newQuery);
40
setResults(searchResults);
41
});
42
};
43
44
return (
45
<div>
46
<input
47
value={query}
48
onChange={(e) => handleSearch(e.target.value)}
49
placeholder="Search..."
50
/>
51
52
{isPending && <div>Searching...</div>}
53
54
<ul>
55
{results.map((result, index) => (
56
<li key={index}>{result}</li>
57
))}
58
</ul>
59
</div>
60
);
61
}
62
63
function performExpensiveSearch(query: string): string[] {
64
// Simulate expensive operation
65
const items = Array.from({ length: 10000 }, (_, i) => `Item ${i}: ${query}`);
66
return items.filter(item => item.includes(query)).slice(0, 100);
67
}
68
```
69
70
### startTransition
71
72
Function for marking updates as non-urgent transitions without using a hook.
73
74
```typescript { .api }
75
/**
76
* Marks state updates as non-urgent transitions
77
* @param callback - Function containing state updates to mark as transition
78
*/
79
function startTransition(callback: () => void): void;
80
```
81
82
**Usage Examples:**
83
84
```typescript
85
import React, { useState, startTransition } from "react";
86
87
function TabContainer() {
88
const [activeTab, setActiveTab] = useState("home");
89
const [tabContent, setTabContent] = useState<React.ReactNode>("Home content");
90
91
const switchTab = (tabId: string) => {
92
// Update active tab immediately (urgent)
93
setActiveTab(tabId);
94
95
// Update content as transition (non-urgent)
96
startTransition(() => {
97
setTabContent(generateExpensiveContent(tabId));
98
});
99
};
100
101
return (
102
<div>
103
<nav>
104
<button
105
onClick={() => switchTab("home")}
106
className={activeTab === "home" ? "active" : ""}
107
>
108
Home
109
</button>
110
<button
111
onClick={() => switchTab("profile")}
112
className={activeTab === "profile" ? "active" : ""}
113
>
114
Profile
115
</button>
116
<button
117
onClick={() => switchTab("settings")}
118
className={activeTab === "settings" ? "active" : ""}
119
>
120
Settings
121
</button>
122
</nav>
123
124
<main>
125
{tabContent}
126
</main>
127
</div>
128
);
129
}
130
131
function generateExpensiveContent(tabId: string): React.ReactNode {
132
// Simulate expensive content generation
133
const items = Array.from({ length: 1000 }, (_, i) => (
134
<div key={i}>{tabId} item {i}</div>
135
));
136
return <div>{items}</div>;
137
}
138
```
139
140
### useDeferredValue
141
142
Hook for deferring non-urgent updates to keep the UI responsive.
143
144
```typescript { .api }
145
/**
146
* Defers a value update to prevent blocking urgent updates
147
* @param value - Value to defer
148
* @param initialValue - Optional initial value used during initial render
149
* @returns Deferred value that may lag behind the actual value
150
*/
151
function useDeferredValue<T>(value: T): T;
152
function useDeferredValue<T>(value: T, initialValue: T): T;
153
```
154
155
**Usage Examples:**
156
157
```typescript
158
import React, { useState, useDeferredValue, useMemo } from "react";
159
160
function ProductList() {
161
const [searchTerm, setSearchTerm] = useState("");
162
const deferredSearchTerm = useDeferredValue(searchTerm);
163
164
// Expensive filtering based on deferred value
165
const filteredProducts = useMemo(() => {
166
console.log("Filtering products...");
167
return PRODUCTS.filter(product =>
168
product.name.toLowerCase().includes(deferredSearchTerm.toLowerCase())
169
);
170
}, [deferredSearchTerm]);
171
172
const isStale = searchTerm !== deferredSearchTerm;
173
174
return (
175
<div>
176
<input
177
value={searchTerm}
178
onChange={(e) => setSearchTerm(e.target.value)}
179
placeholder="Search products..."
180
/>
181
182
<div style={{ opacity: isStale ? 0.5 : 1 }}>
183
{isStale && <div>Updating results...</div>}
184
<ul>
185
{filteredProducts.map(product => (
186
<li key={product.id}>
187
{product.name} - ${product.price}
188
</li>
189
))}
190
</ul>
191
</div>
192
</div>
193
);
194
}
195
196
const PRODUCTS = [
197
{ id: 1, name: "Laptop", price: 999 },
198
{ id: 2, name: "Phone", price: 599 },
199
// ... many more products
200
];
201
```
202
203
### useOptimistic
204
205
Hook for implementing optimistic updates that appear immediately but can be reverted.
206
207
```typescript { .api }
208
/**
209
* Manages optimistic state updates
210
* @param state - Current state
211
* @param updateFn - Function to apply optimistic update
212
* @returns Array with optimistic state and function to add optimistic update
213
*/
214
function useOptimistic<S, A>(
215
state: S,
216
updateFn: (currentState: S, optimisticValue: A) => S
217
): [S, (optimisticValue: A) => void];
218
```
219
220
**Usage Examples:**
221
222
```typescript
223
import React, { useState, useOptimistic } from "react";
224
225
interface Todo {
226
id: string;
227
text: string;
228
completed: boolean;
229
}
230
231
function TodoApp() {
232
const [todos, setTodos] = useState<Todo[]>([]);
233
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
234
todos,
235
(currentTodos, newTodo: Todo) => [...currentTodos, newTodo]
236
);
237
238
const addTodo = async (text: string) => {
239
const newTodo: Todo = {
240
id: `temp-${Date.now()}`,
241
text,
242
completed: false
243
};
244
245
// Show optimistic update immediately
246
addOptimisticTodo(newTodo);
247
248
try {
249
// Send to server
250
const response = await fetch("/api/todos", {
251
method: "POST",
252
headers: { "Content-Type": "application/json" },
253
body: JSON.stringify({ text })
254
});
255
256
if (response.ok) {
257
const savedTodo = await response.json();
258
// Replace optimistic todo with server response
259
setTodos(currentTodos => [...currentTodos, savedTodo]);
260
} else {
261
throw new Error("Failed to save todo");
262
}
263
} catch (error) {
264
// Optimistic update will be automatically reverted
265
console.error("Failed to add todo:", error);
266
}
267
};
268
269
return (
270
<div>
271
<form onSubmit={(e) => {
272
e.preventDefault();
273
const formData = new FormData(e.currentTarget);
274
const text = formData.get("text") as string;
275
if (text.trim()) {
276
addTodo(text.trim());
277
e.currentTarget.reset();
278
}
279
}}>
280
<input name="text" placeholder="Add todo..." />
281
<button type="submit">Add</button>
282
</form>
283
284
<ul>
285
{optimisticTodos.map(todo => (
286
<li
287
key={todo.id}
288
style={{
289
opacity: todo.id.startsWith("temp-") ? 0.7 : 1,
290
fontStyle: todo.id.startsWith("temp-") ? "italic" : "normal"
291
}}
292
>
293
{todo.text}
294
{todo.id.startsWith("temp-") && " (saving...)"}
295
</li>
296
))}
297
</ul>
298
</div>
299
);
300
}
301
```
302
303
## Advanced Concurrency Patterns
304
305
### Priority-Based Updates
306
307
```typescript
308
import React, { useState, useTransition, useDeferredValue, startTransition } from "react";
309
310
function MultiLevelPriorityApp() {
311
// Immediate: User input
312
const [inputValue, setInputValue] = useState("");
313
314
// Deferred: Search results
315
const deferredInput = useDeferredValue(inputValue);
316
317
// Transition: Heavy processing
318
const [processedData, setProcessedData] = useState<any[]>([]);
319
const [isPending, startTransition] = useTransition();
320
321
React.useEffect(() => {
322
// Medium priority: Search results
323
const searchResults = performSearch(deferredInput);
324
325
// Low priority: Heavy data processing
326
startTransition(() => {
327
const processed = performHeavyProcessing(searchResults);
328
setProcessedData(processed);
329
});
330
}, [deferredInput]);
331
332
return (
333
<div>
334
{/* High priority: Always responsive */}
335
<input
336
value={inputValue}
337
onChange={(e) => setInputValue(e.target.value)}
338
placeholder="Type to search..."
339
/>
340
341
{/* Medium priority: May lag slightly */}
342
<div>
343
Search term: {deferredInput}
344
</div>
345
346
{/* Low priority: May be interrupted */}
347
<div>
348
{isPending ? "Processing..." : `Processed ${processedData.length} items`}
349
</div>
350
</div>
351
);
352
}
353
354
function performSearch(query: string) {
355
// Simulate search
356
return query ? [`Result for ${query}`] : [];
357
}
358
359
function performHeavyProcessing(data: any[]) {
360
// Simulate expensive processing
361
return data.map(item => ({ ...item, processed: true, timestamp: Date.now() }));
362
}
363
```
364
365
### Coordinated Transitions
366
367
```typescript
368
import React, { useState, useTransition } from "react";
369
370
function CoordinatedUpdates() {
371
const [view, setView] = useState("list");
372
const [data, setData] = useState<any[]>([]);
373
const [filters, setFilters] = useState<any>({});
374
const [isPending, startTransition] = useTransition();
375
376
const switchView = (newView: string) => {
377
startTransition(() => {
378
// Coordinate multiple state updates in single transition
379
setView(newView);
380
setData(loadDataForView(newView));
381
setFilters(getDefaultFiltersForView(newView));
382
});
383
};
384
385
return (
386
<div>
387
<nav>
388
<button onClick={() => switchView("list")}>List View</button>
389
<button onClick={() => switchView("grid")}>Grid View</button>
390
<button onClick={() => switchView("chart")}>Chart View</button>
391
</nav>
392
393
{isPending && <div>Switching views...</div>}
394
395
<main>
396
<ViewRenderer view={view} data={data} filters={filters} />
397
</main>
398
</div>
399
);
400
}
401
402
function loadDataForView(view: string) {
403
// Simulate loading data specific to view
404
return Array.from({ length: 100 }, (_, i) => ({ id: i, view, data: `${view} item ${i}` }));
405
}
406
407
function getDefaultFiltersForView(view: string) {
408
// Return view-specific default filters
409
return { view, sortOrder: "asc", category: "all" };
410
}
411
412
function ViewRenderer({ view, data, filters }: any) {
413
return <div>{view} view with {data.length} items</div>;
414
}
415
```
416
417
### Interrupted Transitions
418
419
```typescript
420
import React, { useState, useTransition, useEffect } from "react";
421
422
function InterruptibleTransitions() {
423
const [query, setQuery] = useState("");
424
const [results, setResults] = useState<string[]>([]);
425
const [isPending, startTransition] = useTransition();
426
427
useEffect(() => {
428
if (query) {
429
startTransition(() => {
430
// This transition can be interrupted by new queries
431
const searchResults = performSlowSearch(query);
432
setResults(searchResults);
433
});
434
}
435
}, [query]);
436
437
return (
438
<div>
439
<input
440
value={query}
441
onChange={(e) => setQuery(e.target.value)}
442
placeholder="Search (try typing quickly)..."
443
/>
444
445
<div>
446
{isPending ? "Searching..." : `Found ${results.length} results`}
447
</div>
448
449
<ul>
450
{results.slice(0, 10).map((result, index) => (
451
<li key={index}>{result}</li>
452
))}
453
</ul>
454
</div>
455
);
456
}
457
458
function performSlowSearch(query: string): string[] {
459
// Simulate slow search that can be interrupted
460
const start = Date.now();
461
const results = [];
462
463
for (let i = 0; i < 10000; i++) {
464
if (Date.now() - start > 100) break; // Simulate slow operation
465
if (`item ${i}`.includes(query.toLowerCase())) {
466
results.push(`Search result ${i} for "${query}"`);
467
}
468
}
469
470
return results;
471
}
472
```
473
474
## Types
475
476
### Concurrency-Related Types
477
478
```typescript { .api }
479
type TransitionStartFunction = (callback: () => void) => void;
480
481
interface TransitionOptions {
482
name?: string;
483
}
484
485
type DeferredValueHook = {
486
<T>(value: T): T;
487
<T>(value: T, initialValue: T): T;
488
};
489
490
type OptimisticStateHook = <S, A>(
491
state: S,
492
updateFn: (currentState: S, optimisticValue: A) => S
493
) => [S, (optimisticValue: A) => void];
494
```
495
496
## Experimental/Unstable Concurrency Features
497
498
### unstable_addTransitionType
499
500
Adds a transition type for profiling and debugging purposes.
501
502
```typescript { .api }
503
/**
504
* Adds a transition type for profiling
505
* @param type - String identifier for the transition type
506
*/
507
function unstable_addTransitionType(type: string): void;
508
```
509
510
### unstable_getCacheForType
511
512
Gets cache instance for a specific resource type.
513
514
```typescript { .api }
515
/**
516
* Gets cache instance for resource type
517
* @param resourceType - Function that returns resource type
518
* @returns Cache instance for the resource type
519
*/
520
function unstable_getCacheForType<T>(resourceType: () => T): T;
521
```
522
523
### unstable_useCacheRefresh
524
525
Hook for refreshing cache entries.
526
527
```typescript { .api }
528
/**
529
* Hook for refreshing cache
530
* @returns Function to refresh cache entries
531
*/
532
function unstable_useCacheRefresh(): <T>(?() => T, ?T) => void;
533
```
534
535
### unstable_useSwipeTransition
536
537
Hook for gesture-based transitions (experimental).
538
539
```typescript { .api }
540
/**
541
* Hook for swipe gesture transitions
542
* @param gesture - Gesture configuration
543
* @returns Array with pending state and transition function
544
*/
545
function unstable_useSwipeTransition<T>(
546
gesture: StartGesture<T>
547
): [boolean, (callback: () => void, options?: { gesture: T }) => void];
548
549
type StartGesture<T> = any; // Placeholder for gesture type
550
```