diff --git a/src/content/learn/manipulating-the-dom-with-refs.md b/src/content/learn/manipulating-the-dom-with-refs.md index 6d20232fbf7..f7c3c45d5cb 100644 --- a/src/content/learn/manipulating-the-dom-with-refs.md +++ b/src/content/learn/manipulating-the-dom-with-refs.md @@ -352,8 +352,8 @@ However, if you try to put a ref on **your own** component, like ``, ```js import { useRef } from 'react'; -function MyInput(props) { - return ; +function MyInput() { + return ; } export default function MyForm() { @@ -376,40 +376,37 @@ export default function MyForm() { -To help you notice the issue, React also prints an error to the console: +Instead, the application crashes because `inputRef.current` is `null`, due to `` not passing the `ref` prop down to one of its children. - -Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()? - +Cannot read properties of null (reading 'focus') 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. -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: +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. ```js -const MyInput = forwardRef((props, ref) => { - return ; -}); +function MyInput({ ref }) { + return ; +} ``` This is how it works: 1. `` 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. -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`. -3. `MyInput` itself passes the `ref` it received to the `` inside of it. +2. `MyInput` itself passes the `ref` prop it received to the `` inside of it. Now clicking the button to focus the input works: ```js -import { forwardRef, useRef } from 'react'; +import { useRef } from 'react'; -const MyInput = forwardRef((props, ref) => { - return ; -}); +function MyInput({ ref }) { + return ; +} export default function Form() { const inputRef = useRef(null); @@ -442,13 +439,9 @@ In the above example, `MyInput` exposes the original DOM input element. This let ```js -import { - forwardRef, - useRef, - useImperativeHandle -} from 'react'; +import { useRef, useImperativeHandle } from 'react'; -const MyInput = forwardRef((props, ref) => { +function MyInput({ ref }) { const realInputRef = useRef(null); useImperativeHandle(ref, () => ({ // Only expose focus and nothing else @@ -456,8 +449,8 @@ const MyInput = forwardRef((props, ref) => { realInputRef.current.focus(); }, })); - return ; -}); + return ; +} export default function Form() { const inputRef = useRef(null); @@ -691,7 +684,7 @@ However, this doesn't mean that you can't do it at all. It requires caution. **Y - Refs are a generic concept, but most often you'll use them to hold DOM elements. - You instruct React to put a DOM node into `myRef.current` by passing `
`. - Usually, you will use refs for non-destructive actions like focusing, scrolling, or measuring DOM elements. -- 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. +- 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. - Avoid changing DOM nodes managed by React. - If you do modify DOM nodes managed by React, modify parts that React has no reason to update. @@ -1093,7 +1086,7 @@ Make it so that clicking the "Search" button puts focus into the field. Note tha -You'll need `forwardRef` to opt into exposing a DOM node from your own component like `SearchInput`. +You'll need the `ref` prop to opt into exposing a DOM node from your own component like `SearchInput`. @@ -1178,18 +1171,14 @@ export default function SearchButton({ onClick }) { ``` ```js src/SearchInput.js -import { forwardRef } from 'react'; - -export default forwardRef( - function SearchInput(props, ref) { - return ( - - ); - } -); +export default function SearchInput({ ref }) { + return ( + + ); +} ``` ```css diff --git a/src/content/reference/react/useRef.md b/src/content/reference/react/useRef.md index 14cd9b2ecf3..d7c8211e6c1 100644 --- a/src/content/reference/react/useRef.md +++ b/src/content/reference/react/useRef.md @@ -448,16 +448,16 @@ button { display: block; margin-bottom: 20px; } #### Exposing a ref to your own component {/*exposing-a-ref-to-your-own-component*/} -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. +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. ```js -import { forwardRef, useRef } from 'react'; +import { useRef } from 'react'; -const MyInput = forwardRef((props, ref) => { - return ; -}); +function MyInput({ ref }) { + return ; +} export default function Form() { const inputRef = useRef(null); @@ -550,13 +550,7 @@ const inputRef = useRef(null); return ; ``` -You might get an error in the console: - - - -Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()? - - +You might find that `inputRef.current` is always `null`. By default, your own components don't expose refs to the DOM nodes inside them. @@ -573,12 +567,10 @@ export default function MyInput({ value, onChange }) { } ``` -And then wrap it in [`forwardRef`](/reference/react/forwardRef) like this: - -```js {3,8} -import { forwardRef } from 'react'; +And then add a `ref` prop and pass it to the DOM node that you want to reference: -const MyInput = forwardRef(({ value, onChange }, ref) => { +```js {1,6} +export default function MyInput({ value, onChange, ref }) { return ( { ref={ref} /> ); -}); +} export default MyInput; ```