mirror of
https://github.com/reactjs/react.dev.git
synced 2026-02-23 04:12:34 +00:00
Added React.forwardRef to API and docs
This commit is contained in:
22
content/docs/forwarding-refs.md
Normal file
22
content/docs/forwarding-refs.md
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
id: forwarding-refs
|
||||
title: Forwarding Refs
|
||||
permalink: docs/forwarding-refs.html
|
||||
---
|
||||
|
||||
Ref forwarding is a technique for passing a [ref](/docs/refs-and-the-dom.html) through a component to one of its descendants. This technique can be particularly useful with [higher-order components](/docs/higher-order-components.html) (also known as HOCs).
|
||||
|
||||
Let's start with an example HOC that logs component props to the console:
|
||||
`embed:forwarding-refs/log-props-before.js`
|
||||
|
||||
The "logProps" HOC passes all `props` through to the component it wraps, so the rendered output will be the same. For example, we can use this HOC to log all props that get passed to our "fancy button" component:
|
||||
`embed:forwarding-refs/fancy-button.js`
|
||||
|
||||
There is one caveat to the above example: refs will not get passed through. That's because `ref` is not a prop. Like `key`, it's handled differently by React. If you add a ref to a HOC, the ref will refer to the outermost container component, not the wrapped component.
|
||||
|
||||
This means that refs intended for our `FancyButton` component will actually be attached to the `LogProps` component:
|
||||
`embed:forwarding-refs/fancy-button-ref.js`
|
||||
|
||||
Fortunately, we can explicitly forward refs to the inner `FancyButton` component using the `React.forwardRef` API. `React.forwardRef` accepts a render function that receives `props` and `ref` parameters and returns a React node. For example:
|
||||
`embed:forwarding-refs/log-props-after.js`
|
||||
|
||||
@@ -394,30 +394,6 @@ import MyComponent, { someFunction } from './MyComponent.js';
|
||||
|
||||
### Refs Aren't Passed Through
|
||||
|
||||
While the convention for higher-order components is to pass through all props to the wrapped component, it's not possible to pass through refs. That's because `ref` is not really a prop — like `key`, it's handled specially by React. If you add a ref to an element whose component is the result of a HOC, the ref refers to an instance of the outermost container component, not the wrapped component.
|
||||
While the convention for higher-order components is to pass through all props to the wrapped component, this does not work for refs. That's because `ref` is not really a prop — like `key`, it's handled specially by React. If you add a ref to an element whose component is the result of a HOC, the ref refers to an instance of the outermost container component, not the wrapped component.
|
||||
|
||||
If you find yourself facing this problem, the ideal solution is to figure out how to avoid using `ref` at all. Occasionally, users who are new to the React paradigm rely on refs in situations where a prop would work better.
|
||||
|
||||
That said, there are times when refs are a necessary escape hatch — React wouldn't support them otherwise. Focusing an input field is an example where you may want imperative control of a component. In that case, one solution is to pass a ref callback as a normal prop, by giving it a different name:
|
||||
|
||||
```js
|
||||
function Field({ inputRef, ...rest }) {
|
||||
return <input ref={inputRef} {...rest} />;
|
||||
}
|
||||
|
||||
// Wrap Field in a higher-order component
|
||||
const EnhancedField = enhance(Field);
|
||||
|
||||
// Inside a class component's render method...
|
||||
<EnhancedField
|
||||
inputRef={(inputEl) => {
|
||||
// This callback gets passed through as a regular prop
|
||||
this.inputEl = inputEl
|
||||
}}
|
||||
/>
|
||||
|
||||
// Now you can call imperative methods
|
||||
this.inputEl.focus();
|
||||
```
|
||||
|
||||
This is not a perfect solution by any means. We prefer that refs remain a library concern, rather than require you to manually handle them. We are exploring ways to solve this problem so that using a HOC is unobservable.
|
||||
The solution for this problem is to use the `React.forwardRef` API (introduced with React 16.3). [Learn more about it in the forwarding refs section](/docs/forwarding-refs.html).
|
||||
@@ -66,6 +66,8 @@
|
||||
title: Web Components
|
||||
- id: higher-order-components
|
||||
title: Higher-Order Components
|
||||
- id: forwarding-refs
|
||||
title: Forwarding Refs
|
||||
- id: render-props
|
||||
title: Render Props
|
||||
- id: integrating-with-other-libraries
|
||||
|
||||
@@ -49,6 +49,10 @@ See [Using React without JSX](/docs/react-without-jsx.html) for more information
|
||||
|
||||
- [`React.Fragment`](#reactfragment)
|
||||
|
||||
### Other
|
||||
|
||||
- [`React.forwardRef`](#reactforwardref)
|
||||
|
||||
* * *
|
||||
|
||||
## Reference
|
||||
@@ -217,3 +221,10 @@ render() {
|
||||
```
|
||||
|
||||
You can also use it with the shorthand `<></>` syntax. For more information, see [React v16.2.0: Improved Support for Fragments](/blog/2017/11/28/react-v16.2.0-fragment-support.html).
|
||||
|
||||
### `React.forwardRef`
|
||||
|
||||
`React.forwardRef` accepts a render function that receives `props` and `ref` parameters and returns a React node. Ref forwarding is a technique for passing a [ref](/docs/refs-and-the-dom.html) through a component to one of its descendants. This technique can be particularly useful with [higher-order components](/docs/higher-order-components.html):
|
||||
`embed:reference-react-forward-ref.js`
|
||||
|
||||
For more information, see [forwarding refs](/docs/forwarding-refs.html).
|
||||
15
examples/forwarding-refs/fancy-button-ref.js
Normal file
15
examples/forwarding-refs/fancy-button-ref.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import FancyButton from './FancyButton';
|
||||
|
||||
// highlight-next-line
|
||||
const ref = React.createRef();
|
||||
|
||||
// The FancyButton component we imported is the LogProps HOC.
|
||||
// Even though the rendered output will be the same,
|
||||
// Our ref will point to LogProps instead of the inner FancyButton component!
|
||||
// This means we can't call e.g. ref.current.focus()
|
||||
// highlight-range{4}
|
||||
<FancyButton
|
||||
label="Click Me"
|
||||
handleClick={handleClick}
|
||||
ref={ref}
|
||||
/>;
|
||||
12
examples/forwarding-refs/fancy-button.js
Normal file
12
examples/forwarding-refs/fancy-button.js
Normal file
@@ -0,0 +1,12 @@
|
||||
class FancyButton extends React.Component {
|
||||
focus() {
|
||||
// ...
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
// Rather than exporting FancyButton, we export LogProps.
|
||||
// It will render a FancyButton though.
|
||||
// highlight-next-line
|
||||
export default logProps(FancyButton);
|
||||
26
examples/forwarding-refs/log-props-after.js
Normal file
26
examples/forwarding-refs/log-props-after.js
Normal file
@@ -0,0 +1,26 @@
|
||||
function logProps(WrappedComponent) {
|
||||
class LogProps extends React.Component {
|
||||
componentWillReceiveProps(nextProps) {
|
||||
console.log('old props:', this.props);
|
||||
console.log('new props:', nextProps);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {forwardedRef, ...rest} = this.props;
|
||||
|
||||
// Assign the custom prop "forwardedRef" as a ref
|
||||
// highlight-range{1-3}
|
||||
return (
|
||||
<WrappedComponent ref={forwardedRef} {...rest} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Intercept the "ref" and pass it as a custom prop, e.g. "forwardedRef"
|
||||
// highlight-range{1-3}
|
||||
function logPropsForwardRef(props, ref) {
|
||||
return <LogProps {...props} forwardedRef={ref} />;
|
||||
}
|
||||
|
||||
return React.forwardRef(logPropsForwardRef);
|
||||
}
|
||||
16
examples/forwarding-refs/log-props-before.js
Normal file
16
examples/forwarding-refs/log-props-before.js
Normal file
@@ -0,0 +1,16 @@
|
||||
// highlight-next-line
|
||||
function logProps(WrappedComponent) {
|
||||
class LogProps extends React.Component {
|
||||
componentWillReceiveProps(nextProps) {
|
||||
console.log('old props:', this.props);
|
||||
console.log('new props:', nextProps);
|
||||
}
|
||||
|
||||
render() {
|
||||
// highlight-next-line
|
||||
return <WrappedComponent {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
return LogProps;
|
||||
}
|
||||
24
examples/reference-react-forward-ref.js
Normal file
24
examples/reference-react-forward-ref.js
Normal file
@@ -0,0 +1,24 @@
|
||||
function enhance(WrappedComponent) {
|
||||
class Enhanced extends React.Component {
|
||||
// ...
|
||||
|
||||
render() {
|
||||
const {forwardedRef, ...rest} = this.props;
|
||||
|
||||
// Assign the custom prop "forwardedRef" as a ref
|
||||
// highlight-range{1-3}
|
||||
return (
|
||||
<WrappedComponent ref={forwardedRef} {...rest} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Intercept the "ref" and pass it as a custom prop, e.g. "forwardedRef"
|
||||
// highlight-range{1-3}
|
||||
function enhanceForwardRef(props, ref) {
|
||||
return <Enhanced {...props} forwardedRef={ref} />;
|
||||
}
|
||||
|
||||
// highlight-next-line
|
||||
return React.forwardRef(enhanceForwardRef);
|
||||
}
|
||||
Reference in New Issue
Block a user