Skip to content

Commit cabe89e

Browse files
Remove forwardRef referecnes from useRef
1 parent 3b02f82 commit cabe89e

File tree

2 files changed

+15
-135
lines changed

2 files changed

+15
-135
lines changed

src/content/learn/manipulating-the-dom-with-refs.md

Lines changed: 11 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -343,9 +343,11 @@ Read more about [how this helps find bugs](/reference/react/StrictMode#fixing-bu
343343

344344
## Accessing another component's DOM nodes {/*accessing-another-components-dom-nodes*/}
345345

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>
347349

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).
349351

350352
<Sandpack>
351353

@@ -376,88 +378,27 @@ export default function MyForm() {
376378

377379
</Sandpack>
378380

379-
To help you notice the issue, React also prints an error to the console:
380-
381-
<ConsoleBlock level="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-
const MyInput = 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-
const MyInput = forwardRef((props, ref) => {
411-
return <input {...props} ref={ref} />;
412-
});
413-
414-
export default function Form() {
415-
const inputRef = useRef(null);
416-
417-
function handleClick() {
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-
436381
<DeepDive>
437382

438383
#### Exposing a subset of the API with an imperative handle {/*exposing-a-subset-of-the-api-with-an-imperative-handle*/}
439384

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):
441386

442387
<Sandpack>
443388

444389
```js
445-
import {
446-
forwardRef,
447-
useRef,
448-
useImperativeHandle
449-
} from 'react';
390+
import { useRef, useImperativeHandle } from "react";
450391

451-
const MyInput = forwardRef((props, ref) => {
392+
const MyInput = ({ ref }) => {
452393
const realInputRef = useRef(null);
453394
useImperativeHandle(ref, () => ({
454395
// Only expose focus and nothing else
455396
focus() {
456397
realInputRef.current.focus();
457398
},
458399
}));
459-
return <input {...props} ref={realInputRef} />;
460-
});
400+
return <input ref={realInputRef} />;
401+
};
461402

462403
export default function Form() {
463404
const inputRef = useRef(null);
@@ -469,17 +410,15 @@ export default function Form() {
469410
return (
470411
<>
471412
<MyInput ref={inputRef} />
472-
<button onClick={handleClick}>
473-
Focus the input
474-
</button>
413+
<button onClick={handleClick}>Focus the input</button>
475414
</>
476415
);
477416
}
478417
```
479418

480419
</Sandpack>
481420

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.
483422

484423
</DeepDive>
485424

src/content/reference/react/useRef.md

Lines changed: 4 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -448,16 +448,16 @@ button { display: block; margin-bottom: 20px; }
448448
449449
#### Exposing a ref to your own component {/*exposing-a-ref-to-your-own-component*/}
450450
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.
452452
453453
<Sandpack>
454454
455455
```js
456-
import { forwardRef, useRef } from 'react';
456+
import { useRef } from 'react';
457457

458-
const MyInput = forwardRef((props, ref) => {
458+
const MyInput = (props, ref) => {
459459
return <input {...props} ref={ref} />;
460-
});
460+
};
461461

462462
export default function Form() {
463463
const inputRef = useRef(null);
@@ -535,62 +535,3 @@ function Video() {
535535
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.
536536
537537
</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-
const inputRef = 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:
564-
565-
```js
566-
export default function MyInput({ value, onChange }) {
567-
return (
568-
<input
569-
value={value}
570-
onChange={onChange}
571-
/>
572-
);
573-
}
574-
```
575-
576-
And then wrap it in [`forwardRef`](/reference/react/forwardRef) like this:
577-
578-
```js {3,8}
579-
import { forwardRef } from 'react';
580-
581-
const MyInput = forwardRef(({ value, onChange }, ref) => {
582-
return (
583-
<input
584-
value={value}
585-
onChange={onChange}
586-
ref={ref}
587-
/>
588-
);
589-
});
590-
591-
export default MyInput;
592-
```
593-
594-
Then the parent component can get a ref to it.
595-
596-
Read more about [accessing another component's DOM nodes.](/learn/manipulating-the-dom-with-refs#accessing-another-components-dom-nodes)

0 commit comments

Comments
 (0)