Original link: reactjs.org/docs/forwar…
The introduction
Ref forwarding is a technique for automatically passing a Ref from a component to its children. But this is not required in most components. But it can be very useful for some components, especially in some reusable third-party component libraries. Common scenarios are described below.
Forward the refs to the DOM component
Consider the following example, where the FancyButton component renders a native Button DOM element:
function FancyButton(props) {
return (
<button className="FancyButton">
{props.children}
</button>
);
}
Copy the code
React components hide their implementation details, including their render output. Other components that use FancyButton usually do not need to get the ref of the button DOM element inside it. This is great because it prevents components from being overly dependent on the DOM structure of other components.
While this encapsulation of code is ideal for application-level components such as FeedStory or Comment, it can be very inconvenient for highly reusable “leaf” components such as FancyButton or MyTextInput. These components are used in an application like a native DOM button or input, so getting their DOM nodes is an unavoidable operation for managing focus, selection, or animation effects.
Refs forwarding is an opt-in feature that lets components take the Refs they receive and pass (or forward) it to further child elements.
In the following example, FancyButton uses the React. ForwardRef to get the ref passed to it and forward it to the button element it renders.
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton"> {props.children} </button> )); // You can now get references to DOM elements directly: const ref = react.createref (); <FancyButton ref={ref}>Click me! </FancyButton>;Copy the code
In this way, the component that uses the FancyButton component gets a reference to the Button element in the FancyButton component and can use it if necessary — just like manipulating the DOM element directly.
Here is a detailed explanation of how the above example code works:
- We call tondo
React.createRef
To create aReact refAnd assign it toref
The variable. - By declaring JSX properties
<FancyButton ref={ref}>
willref
Passed to theFancyButton
. - The React to
ref
Passed in as the second argumentforwardRef
Within the(props, ref) => ...
Function. - We do this by declaring JSX properties
<button ref={ref}>
willref
Forwarded tobutton
Elements. - When ref is bound, we can pass
ref.current
In order to get<button>
The DOM node.
Note: the second argument ref only exists when you call the react. forwardRef to define the component. Normal function components and class components do not accept ref as a parameter and cannot get ref in props. .ref forwarding is not limited to DOM elements; you can also forward a ref to a class component instance.
Considerations for component library maintainers
When you use the forwardRef in a component library, you should treat it as a disruptive change and release a new version. Because your component library will be significantly different from the previous version (for example, to whom refs are assigned, to which declaration types are exported), this can break the user’s application and components that rely on the previous version.
For the same reason, calling the React forwardRef conditionally when it exists is not recommended: it changes the behavior of your library and breaks the user’s app when the React forwardRef updates.
Forwarding refs in higher-order components
Forwarding refs is useful for higher-order components, also known as HOC. In the following example we use the higher-order component to print the props it receives on the console:
function logProps(WrappedComponent) {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props:', prevProps);
console.log('new props:', this.props);
}
render() {
return <WrappedComponent {...this.props} />;
}
}
return LogProps;
}
Copy the code
The higher-order component logProps passes all props to the component it wraps, so the render will be the same. In the following example, we print all props passed to the “Fancy Button” component using this HOC:
class FancyButton extends React.Component {
focus() {/ /... } / /... } // We passedexportLogProps insteadexportFancyButton // This still renders FancyButtonexport default logProps(FancyButton);
Copy the code
But there is one thing to note in the above example: refs are not passed through because ref is not a PORP. Just like key, React handles it differently. If you add a ref to HOC, that ref will be a reference to the outermost container component, not the wrapped component.
This means that the ref intended for FancyButton is actually bound to the LogProps component:
import FancyButton from './FancyButton'; const ref = React.createRef(); // The FancyButton component we introduced is actually the higher order component of the LogProps // even though the final render is the same, // our ref points to the LogProps instead of the internal FancyButton component! // This means we cannot call ref.current-focus () <FancyButton label="Click Me"
handleClick={handleClick}
ref={ref}
/>;
Copy the code
Fortunately, we can forward refs to the internal FancyButton component by explicitly calling the React.forwardRef API. The forwardRef receives a render function (which takes props and ref as arguments) and returns a React node:
function logProps(Component) {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props:', prevProps);
console.log('new props:', this.props);
}
render() { const {forwardedRef, ... rest} = this.props; // Set the custom "forwardRef" as the refreturn<Component ref={forwardedRef} {... rest} />; }} // Notice the second argument "ref" provided by the forwardRef // we can pass it as a regular prop to the LogProps, such as the forwardRef. // The forwardRef prop will then be bound to the Componentreturn React.forwardRef((props, ref) => {
return<LogProps {... props} forwardedRef={ref} />; }); }Copy the code
Explicitly customize the name in DevTools
React. ForwardRef receives a render function. React DevTools uses this function to determine what to display for the REF forward component.
For example, the component will display the “ForwardRef” in DevTools:
const WrappedComponent = React.forwardRef((props, ref) => {
return<LogProps {... props} forwardedRef={ref} />; });Copy the code
If you give the render function a name, DevTools will have that name (e.g. “ForwardRef(myFunction)”):
const WrappedComponent = React.forwardRef(
function myFunction(props, ref) {
return <LogProps {...props} forwardedRef={ref} />;
}
);
Copy the code
You can even include your wrapped components by setting the function’s displayName property:
function logProps(Component) {
class LogProps extends React.Component {
// ...
}
function forwardRef(props, ref) {
return<LogProps {... props} forwardedRef={ref} />; } // Give this component a presentation name // to make it more helpful to users in DevTools // for example"ForwardRef(logProps(MyComponent))"
const name = Component.displayName || Component.name;
forwardRef.displayName = `logProps(${name}) `;return React.forwardRef(forwardRef);
}
Copy the code