You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/content/learn/manipulating-the-dom-with-refs.md
+11-72Lines changed: 11 additions & 72 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -343,9 +343,11 @@ Read more about [how this helps find bugs](/reference/react/StrictMode#fixing-bu
343
343
344
344
## Accessing another component's DOM nodes {/*accessing-another-components-dom-nodes*/}
345
345
346
-
When you put a ref on a built-in component that outputs a browser element like `<input />`, React will set that ref's `current` property to the corresponding DOM node (such as the actual `<input />` in the browser).
346
+
<Pitfall>
347
+
Refs are an escape hatch that should be used sparingly. Manually manipulating _another_ component's DOM nodes makes your code even more fragile.
348
+
</Pitfall>
347
349
348
-
However, if you try to put a ref on **your own** component, like `<MyInput />`, by default you will get `null`. Here is an example demonstrating it. Notice how clicking the button **does not** focus the input:
350
+
When you put a ref on a built-in component that outputs a browser element like `<input />`, React will set that ref's `current` property to the corresponding DOM node (such as the actual `<input />` in the browser).
349
351
350
352
<Sandpack>
351
353
@@ -376,88 +378,27 @@ export default function MyForm() {
376
378
377
379
</Sandpack>
378
380
379
-
To help you notice the issue, React also prints an error to the console:
380
-
381
-
<ConsoleBlocklevel="error">
382
-
383
-
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
384
-
385
-
</ConsoleBlock>
386
-
387
-
This happens because by default React does not let a component access the DOM nodes of other components. Not even for its own children! This is intentional. Refs are an escape hatch that should be used sparingly. Manually manipulating _another_ component's DOM nodes makes your code even more fragile.
388
-
389
-
Instead, components that _want_ to expose their DOM nodes have to **opt in** to that behavior. A component can specify that it "forwards" its ref to one of its children. Here's how `MyInput` can use the `forwardRef` API:
390
-
391
-
```js
392
-
constMyInput=forwardRef((props, ref) => {
393
-
return<input {...props} ref={ref} />;
394
-
});
395
-
```
396
-
397
-
This is how it works:
398
-
399
-
1.`<MyInput ref={inputRef} />` tells React to put the corresponding DOM node into `inputRef.current`. However, it's up to the `MyInput` component to opt into that--by default, it doesn't.
400
-
2. The `MyInput` component is declared using `forwardRef`. **This opts it into receiving the `inputRef` from above as the second `ref` argument** which is declared after `props`.
401
-
3.`MyInput` itself passes the `ref` it received to the `<input>` inside of it.
402
-
403
-
Now clicking the button to focus the input works:
404
-
405
-
<Sandpack>
406
-
407
-
```js
408
-
import { forwardRef, useRef } from'react';
409
-
410
-
constMyInput=forwardRef((props, ref) => {
411
-
return<input {...props} ref={ref} />;
412
-
});
413
-
414
-
exportdefaultfunctionForm() {
415
-
constinputRef=useRef(null);
416
-
417
-
functionhandleClick() {
418
-
inputRef.current.focus();
419
-
}
420
-
421
-
return (
422
-
<>
423
-
<MyInput ref={inputRef} />
424
-
<button onClick={handleClick}>
425
-
Focus the input
426
-
</button>
427
-
</>
428
-
);
429
-
}
430
-
```
431
-
432
-
</Sandpack>
433
-
434
-
In design systems, it is a common pattern for low-level components like buttons, inputs, and so on, to forward their refs to their DOM nodes. On the other hand, high-level components like forms, lists, or page sections usually won't expose their DOM nodes to avoid accidental dependencies on the DOM structure.
435
-
436
381
<DeepDive>
437
382
438
383
#### Exposing a subset of the API with an imperative handle {/*exposing-a-subset-of-the-api-with-an-imperative-handle*/}
439
384
440
-
In the above example, `MyInput`exposes the original DOM input element. This lets the parent component call `focus()` on it. However, this also lets the parent component do something else--for example, change its CSS styles. In uncommon cases, you may want to restrict the exposed functionality. You can do that with `useImperativeHandle`:
385
+
In the above example, `MyInput`ref props is passed on to the original DOM input element. This lets the parent component call `focus()` on it. However, this also lets the parent component do something else--for example, change its CSS styles. In uncommon cases, you may want to restrict the exposed functionality. You can do that with [`useImperativeHandle`](/reference/react/useImperativeHandle):
@@ -469,17 +410,15 @@ export default function Form() {
469
410
return (
470
411
<>
471
412
<MyInput ref={inputRef} />
472
-
<button onClick={handleClick}>
473
-
Focus the input
474
-
</button>
413
+
<button onClick={handleClick}>Focus the input</button>
475
414
</>
476
415
);
477
416
}
478
417
```
479
418
480
419
</Sandpack>
481
420
482
-
Here, `realInputRef` inside `MyInput` holds the actual input DOM node. However, `useImperativeHandle` instructs React to provide your own special object as the value of a ref to the parent component. So `inputRef.current` inside the `Form` component will only have the `focus` method. In this case, the ref "handle" is not the DOM node, but the custom object you create inside `useImperativeHandle` call.
421
+
Here, `realInputRef` inside `MyInput` holds the actual input DOM node. However, [`useImperativeHandle`](/reference/react/useImperativeHandle) instructs React to provide your own special object as the value of a ref to the parent component. So `inputRef.current` inside the `Form` component will only have the `focus` method. In this case, the ref "handle" is not the DOM node, but the custom object you create inside [`useImperativeHandle`](/reference/react/useImperativeHandle) call.
#### Exposing a ref to your own component {/*exposing-a-ref-to-your-own-component*/}
450
450
451
-
Sometimes, you may want to let the parent component manipulate the DOM inside of your component. For example, maybe you're writing a `MyInput` component, but you want the parent to be able to focus the input (which the parent has no access to). You can use a combination of `useRef` to hold the input and [`forwardRef`](/reference/react/forwardRef) to expose it to the parent component. Read a [detailed walkthrough](/learn/manipulating-the-dom-with-refs#accessing-another-components-dom-nodes) here.
451
+
Sometimes, you may want to let the parent component manipulate the DOM inside of your component. For example, maybe you're writing a `MyInput` component, but you want the parent to be able to focus the input (which the parent has no access to). You can create a `ref` in the parent and pass the `ref` as prop to the child component. Read a [detailed walkthrough](/learn/manipulating-the-dom-with-refs#accessing-another-components-dom-nodes) here.
452
452
453
453
<Sandpack>
454
454
455
455
```js
456
-
import { forwardRef, useRef } from'react';
456
+
import { useRef } from'react';
457
457
458
-
constMyInput=forwardRef((props, ref) => {
458
+
constMyInput= (props, ref) => {
459
459
return<input {...props} ref={ref} />;
460
-
});
460
+
};
461
461
462
462
exportdefaultfunctionForm() {
463
463
constinputRef=useRef(null);
@@ -535,62 +535,3 @@ function Video() {
535
535
Here, the `playerRef` itself is nullable. However, you should be able to convince your type checker that there is no case in which `getPlayer()` returns `null`. Then use `getPlayer()` in your event handlers.
536
536
537
537
</DeepDive>
538
-
539
-
---
540
-
541
-
## Troubleshooting {/*troubleshooting*/}
542
-
543
-
### I can't get a ref to a custom component {/*i-cant-get-a-ref-to-a-custom-component*/}
544
-
545
-
If you try to pass a `ref` to your own component like this:
546
-
547
-
```js
548
-
constinputRef=useRef(null);
549
-
550
-
return<MyInput ref={inputRef} />;
551
-
```
552
-
553
-
You might get an error in the console:
554
-
555
-
<ConsoleBlock level="error">
556
-
557
-
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
558
-
559
-
</ConsoleBlock>
560
-
561
-
By default, your own components don't expose refs to the DOM nodes inside them.
562
-
563
-
To fix this, find the component that you want to get a ref to:
0 commit comments