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:

  1. We call tondoReact.createRefTo create aReact refAnd assign it torefThe variable.
  2. By declaring JSX properties<FancyButton ref={ref}>willrefPassed to theFancyButton.
  3. The React torefPassed in as the second argumentforwardRefWithin the(props, ref) => ...Function.
  4. We do this by declaring JSX properties<button ref={ref}>willrefForwarded tobuttonElements.
  5. When ref is bound, we can passref.currentIn 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