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
+24-68
Original file line number
Diff line number
Diff line change
@@ -343,75 +343,39 @@ 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. Manually manipulating _another_ component's DOM nodes can make your code 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
+
You can pass refs from parent component to child components [just like any other prop](/learn/passing-props-to-a-component).
349
351
350
-
<Sandpack>
351
-
352
-
```js
352
+
```js {3-4,9}
353
353
import { useRef } from'react';
354
354
355
-
functionMyInput(props) {
356
-
return<input {...props} />;
355
+
functionMyInput({ ref }) {
356
+
return<input ref={ref} />;
357
357
}
358
358
359
-
exportdefaultfunctionMyForm() {
359
+
functionMyForm() {
360
360
constinputRef=useRef(null);
361
-
362
-
functionhandleClick() {
363
-
inputRef.current.focus();
364
-
}
365
-
366
-
return (
367
-
<>
368
-
<MyInput ref={inputRef} />
369
-
<button onClick={handleClick}>
370
-
Focus the input
371
-
</button>
372
-
</>
373
-
);
361
+
return<MyInput ref={inputRef} />
374
362
}
375
363
```
376
364
377
-
</Sandpack>
378
-
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.
365
+
In the above example, a ref is created in the parent component, `MyForm`, and is passed to the child component, `MyInput`. `MyInput` then passes the ref to `<input>`. Because `<input>` is a [built-in component](/reference/react-dom/components/common) React sets the `.current` property of the ref to the `<input>` DOM element.
402
366
403
-
Now clicking the button to focusthe input works:
367
+
The `inputRef` created in `MyForm` now points to the `<input>` DOM element returned by `MyInput`. A click handler created in `MyForm` can access `inputRef` and call `focus()` to set the focus on `<input>`.
404
368
405
369
<Sandpack>
406
370
407
371
```js
408
-
import { forwardRef, useRef } from'react';
372
+
import { useRef } from'react';
409
373
410
-
constMyInput=forwardRef((props,ref) => {
411
-
return<input {...props} ref={ref} />;
412
-
});
374
+
functionMyInput({ ref }) {
375
+
return<input ref={ref} />;
376
+
}
413
377
414
-
exportdefaultfunctionForm() {
378
+
exportdefaultfunctionMyForm() {
415
379
constinputRef=useRef(null);
416
380
417
381
functionhandleClick() {
@@ -431,33 +395,27 @@ export default function Form() {
431
395
432
396
</Sandpack>
433
397
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
398
<DeepDive>
437
399
438
400
#### Exposing a subset of the API with an imperative handle {/*exposing-a-subset-of-the-api-with-an-imperative-handle*/}
439
401
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`:
402
+
In the above example, the ref passed to `MyInput`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 +427,15 @@ export default function Form() {
469
427
return (
470
428
<>
471
429
<MyInput ref={inputRef} />
472
-
<button onClick={handleClick}>
473
-
Focus the input
474
-
</button>
430
+
<button onClick={handleClick}>Focus the input</button>
475
431
</>
476
432
);
477
433
}
478
434
```
479
435
480
436
</Sandpack>
481
437
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.
438
+
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.
483
439
484
440
</DeepDive>
485
441
@@ -591,7 +547,7 @@ export default function TodoList() {
#### 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) => {
459
-
return<input {...props} ref={ref} />;
460
-
});
458
+
functionMyInput({ ref }) {
459
+
return<input ref={ref} />;
460
+
};
461
461
462
462
exportdefaultfunctionForm() {
463
463
constinputRef=useRef(null);
@@ -554,7 +554,7 @@ You might get an error in the console:
554
554
555
555
<ConsoleBlock level="error">
556
556
557
-
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
And then wrap it in [`forwardRef`](/reference/react/forwardRef) like this:
577
-
578
-
```js {3,8}
579
-
import { forwardRef } from'react';
576
+
And then add `ref` to the list of props your component accepts and pass `ref` as a prop to the relevent child [built-in component](/reference/react-dom/components/common) like this:
0 commit comments