npm-react

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

How to use

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

refs.md docs/

1
# References & Utilities
2
3
React provides utilities for creating and managing references to DOM elements and component instances, along with other helpful utility functions.
4
5
## Capabilities
6
7
### createRef
8
9
Creates a ref object for class components to access DOM elements or component instances.
10
11
```javascript { .api }
12
/**
13
* Creates a ref object for class components
14
* @returns Ref object with current property
15
*/
16
function createRef<T = any>(): RefObject<T>;
17
18
interface RefObject<T> {
19
readonly current: T | null;
20
}
21
```
22
23
**Usage Examples:**
24
25
```javascript
26
import React, { createRef, Component } from 'react';
27
28
// Basic DOM element access in class component
29
class InputComponent extends Component {
30
constructor(props) {
31
super(props);
32
this.inputRef = createRef();
33
}
34
35
componentDidMount() {
36
// Focus input when component mounts
37
this.inputRef.current.focus();
38
}
39
40
getValue = () => {
41
return this.inputRef.current.value;
42
};
43
44
clear = () => {
45
this.inputRef.current.value = '';
46
this.inputRef.current.focus();
47
};
48
49
render() {
50
return (
51
<div>
52
<input
53
ref={this.inputRef}
54
type="text"
55
placeholder="Enter text"
56
/>
57
<button onClick={this.clear}>Clear</button>
58
</div>
59
);
60
}
61
}
62
63
// Multiple refs in class component
64
class FormComponent extends Component {
65
constructor(props) {
66
super(props);
67
this.nameRef = createRef();
68
this.emailRef = createRef();
69
this.submitButtonRef = createRef();
70
}
71
72
handleSubmit = (e) => {
73
e.preventDefault();
74
75
const formData = {
76
name: this.nameRef.current.value,
77
email: this.emailRef.current.value
78
};
79
80
if (!formData.name) {
81
this.nameRef.current.focus();
82
return;
83
}
84
85
if (!formData.email) {
86
this.emailRef.current.focus();
87
return;
88
}
89
90
this.props.onSubmit(formData);
91
};
92
93
render() {
94
return (
95
<form onSubmit={this.handleSubmit}>
96
<input
97
ref={this.nameRef}
98
type="text"
99
placeholder="Name"
100
required
101
/>
102
103
<input
104
ref={this.emailRef}
105
type="email"
106
placeholder="Email"
107
required
108
/>
109
110
<button ref={this.submitButtonRef} type="submit">
111
Submit
112
</button>
113
</form>
114
);
115
}
116
}
117
118
// Ref for custom component instance
119
class CustomButton extends Component {
120
focus = () => {
121
this.buttonRef.current.focus();
122
};
123
124
click = () => {
125
this.buttonRef.current.click();
126
};
127
128
constructor(props) {
129
super(props);
130
this.buttonRef = createRef();
131
}
132
133
render() {
134
return (
135
<button
136
ref={this.buttonRef}
137
className="custom-button"
138
onClick={this.props.onClick}
139
>
140
{this.props.children}
141
</button>
142
);
143
}
144
}
145
146
class ParentComponent extends Component {
147
constructor(props) {
148
super(props);
149
this.customButtonRef = createRef();
150
}
151
152
handleFocusButton = () => {
153
this.customButtonRef.current.focus();
154
};
155
156
render() {
157
return (
158
<div>
159
<CustomButton ref={this.customButtonRef} onClick={this.handleClick}>
160
Click Me
161
</CustomButton>
162
163
<button onClick={this.handleFocusButton}>
164
Focus Custom Button
165
</button>
166
</div>
167
);
168
}
169
}
170
```
171
172
### Ref Callbacks
173
174
Alternative to ref objects using callback functions for more control over ref assignment.
175
176
```javascript { .api }
177
/**
178
* Callback ref function type
179
* @param instance - DOM element or component instance
180
*/
181
type RefCallback<T> = (instance: T | null) => void;
182
```
183
184
**Usage Examples:**
185
186
```javascript
187
import React, { Component } from 'react';
188
189
// Callback ref with storage
190
class CallbackRefExample extends Component {
191
constructor(props) {
192
super(props);
193
this.inputElement = null;
194
}
195
196
setInputRef = (element) => {
197
this.inputElement = element;
198
199
if (element) {
200
console.log('Input ref attached:', element);
201
// Perform setup when ref is attached
202
element.addEventListener('keydown', this.handleKeyDown);
203
} else {
204
console.log('Input ref detached');
205
// Cleanup when ref is detached
206
if (this.inputElement) {
207
this.inputElement.removeEventListener('keydown', this.handleKeyDown);
208
}
209
}
210
};
211
212
handleKeyDown = (event) => {
213
if (event.key === 'Enter') {
214
this.handleSubmit();
215
}
216
};
217
218
handleSubmit = () => {
219
if (this.inputElement) {
220
console.log('Input value:', this.inputElement.value);
221
}
222
};
223
224
render() {
225
return (
226
<div>
227
<input
228
ref={this.setInputRef}
229
type="text"
230
placeholder="Press Enter to submit"
231
/>
232
</div>
233
);
234
}
235
}
236
237
// Conditional ref assignment
238
class ConditionalRefExample extends Component {
239
constructor(props) {
240
super(props);
241
this.state = { shouldFocusOnMount: true };
242
}
243
244
setInputRef = (element) => {
245
if (element && this.state.shouldFocusOnMount) {
246
element.focus();
247
this.setState({ shouldFocusOnMount: false });
248
}
249
};
250
251
render() {
252
return (
253
<input
254
ref={this.setInputRef}
255
type="text"
256
placeholder="Auto-focused on mount"
257
/>
258
);
259
}
260
}
261
262
// Dynamic ref management
263
class DynamicRefsExample extends Component {
264
constructor(props) {
265
super(props);
266
this.itemRefs = new Map();
267
}
268
269
setItemRef = (id) => (element) => {
270
if (element) {
271
this.itemRefs.set(id, element);
272
} else {
273
this.itemRefs.delete(id);
274
}
275
};
276
277
scrollToItem = (id) => {
278
const element = this.itemRefs.get(id);
279
if (element) {
280
element.scrollIntoView({ behavior: 'smooth' });
281
}
282
};
283
284
render() {
285
const { items } = this.props;
286
287
return (
288
<div>
289
<div className="item-list">
290
{items.map(item => (
291
<div
292
key={item.id}
293
ref={this.setItemRef(item.id)}
294
className="item"
295
>
296
{item.content}
297
</div>
298
))}
299
</div>
300
301
<div className="controls">
302
{items.map(item => (
303
<button
304
key={item.id}
305
onClick={() => this.scrollToItem(item.id)}
306
>
307
Go to {item.id}
308
</button>
309
))}
310
</div>
311
</div>
312
);
313
}
314
}
315
```
316
317
### useId
318
319
Generates stable unique IDs that are consistent between server and client rendering.
320
321
```javascript { .api }
322
/**
323
* Generates stable unique IDs for accessibility
324
* @returns Unique ID string
325
*/
326
function useId(): string;
327
```
328
329
**Usage Examples:**
330
331
```javascript
332
import React, { useId } from 'react';
333
334
// Form field with accessibility
335
function FormField({ label, type = 'text', required, error, ...props }) {
336
const id = useId();
337
const errorId = `${id}-error`;
338
const helpId = `${id}-help`;
339
340
return (
341
<div className="form-field">
342
<label htmlFor={id}>
343
{label}
344
{required && <span className="required" aria-label="required">*</span>}
345
</label>
346
347
<input
348
id={id}
349
type={type}
350
aria-describedby={error ? errorId : (props.help ? helpId : undefined)}
351
aria-invalid={!!error}
352
{...props}
353
/>
354
355
{props.help && (
356
<div id={helpId} className="help-text">
357
{props.help}
358
</div>
359
)}
360
361
{error && (
362
<div id={errorId} className="error-message" role="alert">
363
{error}
364
</div>
365
)}
366
</div>
367
);
368
}
369
370
// Usage
371
function LoginForm() {
372
return (
373
<form>
374
<FormField
375
label="Email"
376
type="email"
377
required
378
help="We'll never share your email"
379
/>
380
381
<FormField
382
label="Password"
383
type="password"
384
required
385
error="Password must be at least 8 characters"
386
/>
387
</form>
388
);
389
}
390
391
// Multiple IDs in one component
392
function TabPanel({ tabs, activeTab, onTabChange }) {
393
const tablistId = useId();
394
const panelIdPrefix = useId();
395
396
return (
397
<div>
398
<div role="tablist" id={tablistId} aria-label="Content tabs">
399
{tabs.map((tab, index) => (
400
<button
401
key={index}
402
role="tab"
403
id={`${tablistId}-tab-${index}`}
404
aria-controls={`${panelIdPrefix}-panel-${index}`}
405
aria-selected={index === activeTab}
406
onClick={() => onTabChange(index)}
407
>
408
{tab.title}
409
</button>
410
))}
411
</div>
412
413
{tabs.map((tab, index) => (
414
<div
415
key={index}
416
role="tabpanel"
417
id={`${panelIdPrefix}-panel-${index}`}
418
aria-labelledby={`${tablistId}-tab-${index}`}
419
hidden={index !== activeTab}
420
>
421
{tab.content}
422
</div>
423
))}
424
</div>
425
);
426
}
427
428
// Dialog with proper ARIA relationships
429
function Dialog({ isOpen, title, children, onClose }) {
430
const dialogId = useId();
431
const titleId = useId();
432
const descriptionId = useId();
433
434
if (!isOpen) return null;
435
436
return (
437
<div
438
role="dialog"
439
id={dialogId}
440
aria-labelledby={titleId}
441
aria-describedby={descriptionId}
442
aria-modal="true"
443
>
444
<h2 id={titleId}>{title}</h2>
445
446
<div id={descriptionId}>
447
{children}
448
</div>
449
450
<button onClick={onClose} aria-label="Close dialog">
451
×
452
</button>
453
</div>
454
);
455
}
456
457
// Nested component IDs
458
function NestedForm() {
459
const formId = useId();
460
461
return (
462
<form id={formId}>
463
<fieldset>
464
<legend>Personal Information</legend>
465
<FormField label="First Name" required />
466
<FormField label="Last Name" required />
467
</fieldset>
468
469
<fieldset>
470
<legend>Contact Information</legend>
471
<FormField label="Email" type="email" required />
472
<FormField label="Phone" type="tel" />
473
</fieldset>
474
</form>
475
);
476
}
477
```
478
479
### act (Testing Utility)
480
481
Ensures that updates related to "units" of interaction with a user interface have been processed and applied to the DOM before making assertions.
482
483
```javascript { .api }
484
/**
485
* Wraps test interactions to ensure proper effect flushing
486
* @param callback - Function containing component interactions
487
* @returns Promise that resolves when effects are flushed
488
*/
489
function act(callback: () => void | Promise<void>): Promise<void>;
490
```
491
492
**Usage Examples:**
493
494
```javascript
495
import React, { useState, useEffect } from 'react';
496
import { act, render, fireEvent } from '@testing-library/react';
497
498
// Component for testing
499
function Counter() {
500
const [count, setCount] = useState(0);
501
const [message, setMessage] = useState('');
502
503
useEffect(() => {
504
setMessage(`Count is ${count}`);
505
}, [count]);
506
507
return (
508
<div>
509
<p data-testid="count">{count}</p>
510
<p data-testid="message">{message}</p>
511
<button onClick={() => setCount(count + 1)}>Increment</button>
512
</div>
513
);
514
}
515
516
// Test with act
517
test('counter updates correctly', async () => {
518
const { getByTestId, getByRole } = render(<Counter />);
519
const button = getByRole('button', { name: 'Increment' });
520
521
// Wrap interactions in act
522
await act(async () => {
523
fireEvent.click(button);
524
});
525
526
expect(getByTestId('count')).toHaveTextContent('1');
527
expect(getByTestId('message')).toHaveTextContent('Count is 1');
528
});
529
530
// Component with async effects
531
function AsyncDataComponent({ userId }) {
532
const [user, setUser] = useState(null);
533
const [loading, setLoading] = useState(true);
534
535
useEffect(() => {
536
let cancelled = false;
537
538
fetchUser(userId).then(userData => {
539
if (!cancelled) {
540
setUser(userData);
541
setLoading(false);
542
}
543
});
544
545
return () => {
546
cancelled = true;
547
};
548
}, [userId]);
549
550
if (loading) return <div>Loading...</div>;
551
552
return <div>User: {user.name}</div>;
553
}
554
555
// Test async effects with act
556
test('loads user data', async () => {
557
const mockUser = { id: 1, name: 'John Doe' };
558
jest.spyOn(global, 'fetchUser').mockResolvedValue(mockUser);
559
560
const { getByText } = render(<AsyncDataComponent userId={1} />);
561
562
expect(getByText('Loading...')).toBeInTheDocument();
563
564
// Wait for async effects to complete
565
await act(async () => {
566
await new Promise(resolve => setTimeout(resolve, 0));
567
});
568
569
expect(getByText('User: John Doe')).toBeInTheDocument();
570
});
571
572
// Testing state updates
573
test('handles multiple state updates', async () => {
574
function MultiStateComponent() {
575
const [count, setCount] = useState(0);
576
const [doubled, setDoubled] = useState(0);
577
578
const handleClick = () => {
579
setCount(c => c + 1);
580
setDoubled(c => (c + 1) * 2);
581
};
582
583
return (
584
<div>
585
<span data-testid="count">{count}</span>
586
<span data-testid="doubled">{doubled}</span>
587
<button onClick={handleClick}>Update</button>
588
</div>
589
);
590
}
591
592
const { getByTestId, getByRole } = render(<MultiStateComponent />);
593
594
await act(async () => {
595
fireEvent.click(getByRole('button'));
596
});
597
598
expect(getByTestId('count')).toHaveTextContent('1');
599
expect(getByTestId('doubled')).toHaveTextContent('2');
600
});
601
```
602
603
### version
604
605
The React library version string.
606
607
```javascript { .api }
608
/**
609
* React library version string
610
*/
611
const version: string;
612
```
613
614
**Usage Examples:**
615
616
```javascript
617
import React from 'react';
618
619
// Access version from React object
620
console.log(`React version: ${React.version}`);
621
622
// Or import directly
623
import { version } from 'react';
624
console.log(`React version: ${version}`);
625
626
// Feature detection based on version
627
function checkReactVersion() {
628
const [major, minor] = version.split('.').map(Number);
629
630
if (major >= 18) {
631
console.log('React 18+ features available');
632
return 'concurrent';
633
} else if (major >= 17) {
634
console.log('React 17+ features available');
635
return 'modern';
636
} else {
637
console.log('Older React version');
638
return 'legacy';
639
}
640
}
641
642
// Runtime version logging
643
function logReactInfo() {
644
console.log({
645
reactVersion: version,
646
buildMode: process.env.NODE_ENV,
647
timestamp: new Date().toISOString()
648
});
649
}
650
```
651
652
## Types
653
654
```javascript { .api }
655
// Ref types
656
interface RefObject<T> {
657
readonly current: T | null;
658
}
659
660
interface MutableRefObject<T> {
661
current: T;
662
}
663
664
type Ref<T> = RefCallback<T> | RefObject<T> | null;
665
type RefCallback<T> = (instance: T | null) => void;
666
type LegacyRef<T> = string | Ref<T>;
667
668
// Create ref function
669
function createRef<T = any>(): RefObject<T>;
670
671
// Hook for IDs
672
function useId(): string;
673
674
// Testing utility
675
function act(callback: () => void | Promise<void>): Promise<void>;
676
677
// Version
678
const version: string;
679
```