This commit is contained in:
Dan Abramov
2022-06-09 19:41:28 +01:00
parent 2b9ee678e9
commit 4a76e101d1
2 changed files with 43 additions and 129 deletions

View File

@@ -1,164 +1,78 @@
---
title: forwardref
title: forwardRef()
---
<Intro>
`forwardref` forwards the [ref](api/useref) attribute it receives to a child React component.
`forwardRef()` lets your component receive a [ref](/learn/manipulating-the-dom-with-refs) and forward it to a child component.
```js
const MyInputComponent= forwardRef((props, ref) => (return component));
const SomeComponent = forwardRef((props, ref) => {
// ...
});
```
</Intro>
- [Usage](#usage)
- [Forwarding refs to the DOM](#forwarding-refs-to-the-DOM)
- [`forwardref` with useImperativeHandle hook](#forwardref-with-useImperativeHandle-hook)
- [Reference](#reference)
- [`const MyInputComponent = forwardRef((props, ref) => {return <input {...props} ref={ref} />;});`](#forwardref)
---
## Usage {/*usage*/}
### Forwarding refs to the DOM {/*forwarding-refs-to-the-DOM*/}
### Exposing a DOM node to the parent component {/*exposing-a-dom-node-to-the-parent-component*/}
Call `forwardref` to wrap a component that needs a `ref` to be passed to it by another component.
By default, components don't expose their DOM nodes. To opt in, wrap your component in a `forwardRef` call:
```js
import { forwardRef, useRef } from 'react';
const MyInputComponent = forwardRef((props, ref) => {
return <input {...props} ref={ref} />;
});
```
The forwardref takes two arguments (props, ref). The second ref argument only exists when you define a component with forwardRef call. This way MyInputComponent can get reference to the underlying buttons DOM node—if necessary. By default, the MyInputComponent component does not opt into using ref.
```js
function handleClick() {
inputRef.current.focus();
}
return (
<>
< MyInputComponent ref={inputRef} />
<button onClick={handleClick}>
Focus the input
</button>
</>
);
//...
```
Note: Ref forwarding is an opt-in feature that lets some components take a ref they receive, and pass it further down (in other words, “forward” it) to a child.
Read more about [accessing another components DOM nodes](https://beta.reactjs.org/learn/manipulating-the-dom-with-refs#accessing-another-components-dom-nodes) and see examples.
<Sandpack>
```js
import { forwardRef, useRef } from 'react';
```js {3,11}
import { forwardRef } from 'react';
const MyInput = forwardRef((props, ref) => {
return <input {...props} ref={ref} />;
});
export default function Form() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
const { label, ...otherProps } = props;
return (
<>
<MyInput ref={inputRef} />
<button onClick={handleClick}>
Focus the input
</button>
</>
<label>
{label}
<input {...otherProps} />
</label>
);
}
});
```
</Sandpack>
---
You will receive a <CodeStep step={1}>ref</CodeStep> as the second argument after props. Pass it to the DOM node that you want to expose:
### `forwardref` with useImperativeHandle hook {/*fforwardref-with-useImperativeHandle-hook*/}
`forwardref` with `useImperativeHandle` hook is another usage for `forwardref`.
When we use `forwardref` and child component receieves the `ref`from its parent component, there is a possiblity that parent component can manipulate the child components. It would be ideal to limit the exposed functionality to only the ones that are necessary.
The restriction of exposing functionality to parent components can be achieved by using 'useImperativeHandle' hook.
<Sandpack>
```js
import {
forwardRef,
useRef,
useImperativeHandle
} from 'react';
```js {8} [[1, 3, "ref"], [1, 8, "ref", 30]]
import { forwardRef } from 'react';
const MyInput = forwardRef((props, ref) => {
const realInputRef = useRef(null);
useImperativeHandle(ref, () => ({
// Only expose focus and nothing else
focus() {
realInputRef.current.focus();
},
}));
return <input {...props} ref={realInputRef} />;
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} ref={ref} />
</label>
);
});
```
export default function Form() {
const inputRef = useRef(null);
Now the parent `Form` component can access `MyInput`'s internal <CodeStep step={2}>`<input>` DOM node</CodeStep> by [passing a ref](/apis/useref#manipulating-the-dom-with-a-ref) to it:
```js [[1, 2, "ref"], [1, 10, "ref", 30], [2, 5, "ref.current"]]
function Form() {
const ref = useRef(null);
function handleClick() {
inputRef.current.focus();
ref.current.focus();
}
return (
<>
<MyInput ref={inputRef} />
<button onClick={handleClick}>
Focus the input
</button>
</>
);
<form>
<MyInput label="Enter your name" ref={ref} />
<button onClick={handleClick}>Edit</button>
</form>
)
}
```
</Sandpack>
---
This `Form` component passes a ref to `MyInput`. The `MyInput` component *forwards* that ref to the `<input>` browser tag. As a result, the `Form` component can access the `<input>` DOM node and call [`focus()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on it.
## Reference {/*reference*/}
Keep in mind that by exposing a ref to the DOM node inside your component, you're making it harder to change your component's internals later. You will typically expose DOM nodes from reusable low-level components like buttons or text inputs, but you won't do it for application-level components like an avatar or a comment.
### `const MyInputComponent = forwardRef((props, ref) => {return <input {...props} ref={ref} />;});` {/*forwardref*/}
Call `forwardref` to wrap a component that needs ref to be passed to it by another component.
```js
import { forwardRef, useRef } from 'react';
const MyInputComponent = forwardRef((props, ref) => {
return <input {...props} ref={ref} />;
});
```
#### Parameters {/*parameters*/}
`forwardRef` accepts a rendering function as an argument. React will call this function with props and ref as two arguments.
forwardRef((props, ref) => {return <input {...props} ref={ref} />;}
#### Returns {/*returns*/}
`forwardref` returns a React node.

View File

@@ -15,6 +15,10 @@
"title": "createContext()",
"path": "/apis/createcontext"
},
{
"title": "forwardRef()",
"path": "/apis/forwardref"
},
{
"title": "useContext()",
"path": "/apis/usecontext"
@@ -34,10 +38,6 @@
{
"title": "useState()",
"path": "/apis/usestate"
},
{
"title": "forwardref",
"path": "/apis/forwardref"
}
]
},