Skip to content

Commit beaab2a

Browse files
committed
update useRef documentation to use ref prop instead of forwardRef
1 parent 69edd84 commit beaab2a

File tree

2 files changed

+62
-87
lines changed

2 files changed

+62
-87
lines changed

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

Lines changed: 39 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -165,27 +165,27 @@ export default function CatFriends() {
165165

166166
```css
167167
div {
168-
width: 100%;
169-
overflow: hidden;
168+
width: 100%;
169+
overflow: hidden;
170170
}
171171

172172
nav {
173-
text-align: center;
173+
text-align: center;
174174
}
175175

176176
button {
177-
margin: .25rem;
177+
margin: .25rem;
178178
}
179179

180180
ul,
181181
li {
182-
list-style: none;
183-
white-space: nowrap;
182+
list-style: none;
183+
white-space: nowrap;
184184
}
185185

186186
li {
187-
display: inline;
188-
padding: 0.5rem;
187+
display: inline;
188+
padding: 0.5rem;
189189
}
190190
```
191191

@@ -285,27 +285,27 @@ function setupCatList() {
285285

286286
```css
287287
div {
288-
width: 100%;
289-
overflow: hidden;
288+
width: 100%;
289+
overflow: hidden;
290290
}
291291

292292
nav {
293-
text-align: center;
293+
text-align: center;
294294
}
295295

296296
button {
297-
margin: .25rem;
297+
margin: .25rem;
298298
}
299299

300300
ul,
301301
li {
302-
list-style: none;
303-
white-space: nowrap;
302+
list-style: none;
303+
white-space: nowrap;
304304
}
305305

306306
li {
307-
display: inline;
308-
padding: 0.5rem;
307+
display: inline;
308+
padding: 0.5rem;
309309
}
310310
```
311311

@@ -352,15 +352,15 @@ However, if you try to put a ref on **your own** component, like `<MyInput />`,
352352
```js
353353
import { useRef } from 'react';
354354

355-
function MyInput(props) {
356-
return <input {...props} />;
355+
function MyInput() {
356+
return <input />;
357357
}
358358

359359
export default function MyForm() {
360360
const inputRef = useRef(null);
361361

362362
function handleClick() {
363-
inputRef.current.focus();
363+
inputRef.current?.focus();
364364
}
365365

366366
return (
@@ -376,40 +376,31 @@ export default function MyForm() {
376376
377377
</Sandpack>
378378
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-
387379
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.
388380
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:
381+
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 by passing the `ref` prop down.
390382
391383
```js
392-
const MyInput = forwardRef((props, ref) => {
384+
function MyInput({ ref, ...props }) {
393385
return <input {...props} ref={ref} />;
394-
});
386+
}
395387
```
396388
397389
This is how it works:
398390
399391
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.
392+
2. `MyInput` itself passes the `ref` prop it received to the `<input>` inside of it.
402393
403394
Now clicking the button to focus the input works:
404395
405396
<Sandpack>
406397
407398
```js
408-
import { forwardRef, useRef } from 'react';
399+
import { useRef } from 'react';
409400

410-
const MyInput = forwardRef((props, ref) => {
401+
function MyInput({ ref, ...props }) {
411402
return <input {...props} ref={ref} />;
412-
});
403+
}
413404

414405
export default function Form() {
415406
const inputRef = useRef(null);
@@ -442,13 +433,9 @@ In the above example, `MyInput` exposes the original DOM input element. This let
442433
<Sandpack>
443434
444435
```js
445-
import {
446-
forwardRef,
447-
useRef,
448-
useImperativeHandle
449-
} from 'react';
436+
import { useRef, useImperativeHandle } from 'react';
450437

451-
const MyInput = forwardRef((props, ref) => {
438+
function MyInput({ ref, ...props }) {
452439
const realInputRef = useRef(null);
453440
useImperativeHandle(ref, () => ({
454441
// Only expose focus and nothing else
@@ -457,7 +444,7 @@ const MyInput = forwardRef((props, ref) => {
457444
},
458445
}));
459446
return <input {...props} ref={realInputRef} />;
460-
});
447+
}
461448

462449
export default function Form() {
463450
const inputRef = useRef(null);
@@ -691,7 +678,7 @@ However, this doesn't mean that you can't do it at all. It requires caution. **Y
691678
- Refs are a generic concept, but most often you'll use them to hold DOM elements.
692679
- You instruct React to put a DOM node into `myRef.current` by passing `<div ref={myRef}>`.
693680
- Usually, you will use refs for non-destructive actions like focusing, scrolling, or measuring DOM elements.
694-
- A component doesn't expose its DOM nodes by default. You can opt into exposing a DOM node by using `forwardRef` and passing the second `ref` argument down to a specific node.
681+
- A component doesn't expose its DOM nodes by default. You can opt into exposing a DOM node by using a `ref` prop and passing it down to a specific node.
695682
- Avoid changing DOM nodes managed by React.
696683
- If you do modify DOM nodes managed by React, modify parts that React has no reason to update.
697684
@@ -1093,7 +1080,7 @@ Make it so that clicking the "Search" button puts focus into the field. Note tha
10931080
10941081
<Hint>
10951082
1096-
You'll need `forwardRef` to opt into exposing a DOM node from your own component like `SearchInput`.
1083+
You'll need the `ref` prop to opt into exposing a DOM node from your own component like `SearchInput`.
10971084
10981085
</Hint>
10991086
@@ -1178,18 +1165,14 @@ export default function SearchButton({ onClick }) {
11781165
```
11791166
11801167
```js src/SearchInput.js
1181-
import { forwardRef } from 'react';
1182-
1183-
export default forwardRef(
1184-
function SearchInput(props, ref) {
1185-
return (
1186-
<input
1187-
ref={ref}
1188-
placeholder="Looking for something?"
1189-
/>
1190-
);
1191-
}
1192-
);
1168+
export default function SearchInput({ ref }) {
1169+
return (
1170+
<input
1171+
ref={ref}
1172+
placeholder="Looking for something?"
1173+
/>
1174+
);
1175+
}
11931176
```
11941177
11951178
```css

src/content/reference/react/useRef.md

Lines changed: 23 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { useRef } from 'react';
2828
function MyComponent() {
2929
const intervalRef = useRef(0);
3030
const inputRef = useRef(null);
31-
// ...
31+
// ...
3232
```
3333
3434
[See more examples below.](#usage)
@@ -65,7 +65,7 @@ import { useRef } from 'react';
6565

6666
function Stopwatch() {
6767
const intervalRef = useRef(0);
68-
// ...
68+
// ...
6969
```
7070
7171
`useRef` returns a <CodeStep step={1}>ref object</CodeStep> with a single <CodeStep step={2}>`current` property</CodeStep> initially set to the <CodeStep step={3}>initial value</CodeStep> you provided.
@@ -245,22 +245,22 @@ import { useRef } from 'react';
245245

246246
function MyComponent() {
247247
const inputRef = useRef(null);
248-
// ...
248+
// ...
249249
```
250250
251251
Then pass your ref object as the `ref` attribute to the JSX of the DOM node you want to manipulate:
252252
253253
```js [[1, 2, "inputRef"]]
254254
// ...
255-
return <input ref={inputRef} />;
255+
return <input ref={inputRef} />;
256256
```
257257
258258
After React creates the DOM node and puts it on the screen, React will set the <CodeStep step={2}>`current` property</CodeStep> of your ref object to that DOM node. Now you can access the `<input>`'s DOM node and call methods like [`focus()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus):
259259
260260
```js [[2, 2, "inputRef.current"]]
261261
function handleClick() {
262-
inputRef.current.focus();
263-
}
262+
inputRef.current.focus();
263+
}
264264
```
265265
266266
React will set the `current` property back to `null` when the node is removed from the screen.
@@ -365,27 +365,27 @@ export default function CatFriends() {
365365
366366
```css
367367
div {
368-
width: 100%;
369-
overflow: hidden;
368+
width: 100%;
369+
overflow: hidden;
370370
}
371371

372372
nav {
373-
text-align: center;
373+
text-align: center;
374374
}
375375

376376
button {
377-
margin: .25rem;
377+
margin: .25rem;
378378
}
379379

380380
ul,
381381
li {
382-
list-style: none;
383-
white-space: nowrap;
382+
list-style: none;
383+
white-space: nowrap;
384384
}
385385

386386
li {
387-
display: inline;
388-
padding: 0.5rem;
387+
display: inline;
388+
padding: 0.5rem;
389389
}
390390
```
391391
@@ -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 use a combination of `useRef` to hold the input and the `ref` prop to expose it to the parent 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+
function MyInput({ ref, ...props }) {
459459
return <input {...props} ref={ref} />;
460-
});
460+
}
461461

462462
export default function Form() {
463463
const inputRef = useRef(null);
@@ -550,13 +550,7 @@ const inputRef = useRef(null);
550550
return <MyInput ref={inputRef} />;
551551
```
552552
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>
553+
You might find that `inputRef.current` is always `null`.
560554
561555
By default, your own components don't expose refs to the DOM nodes inside them.
562556
@@ -573,20 +567,18 @@ export default function MyInput({ value, onChange }) {
573567
}
574568
```
575569
576-
And then wrap it in [`forwardRef`](/reference/react/forwardRef) like this:
577-
578-
```js {3,8}
579-
import { forwardRef } from 'react';
570+
And then add a `ref` prop and pass it to the DOM node that you want to reference:
580571
581-
const MyInput = forwardRef(({ value, onChange }, ref) => {
572+
```js {1,6}
573+
export default function MyInput({ value, onChange, ref }) {
582574
return (
583575
<input
584576
value={value}
585577
onChange={onChange}
586578
ref={ref}
587579
/>
588580
);
589-
});
581+
}
590582

591583
export default MyInput;
592584
```

0 commit comments

Comments
 (0)