What is Ref forwarding?

Ref forwarding is a technique for automatically passing a Ref through a component to one of its children.

Forward refs to the DOM component

  • The following component is a FancyButton component that renders the native DOM element Button
function FancyButton(props) {
  return (
    <button className="FancyButton">
      {props.children}
    </button>
  );
}
Copy the code
  • Ref forwarding is an optional feature that allows some components to receive refs and pass them down to child components.

The react. forwardRef is used because functional components cannot receive refs directly.

  • In the following example, the FancyButton uses the React. ForwardRef to fetch the ref passed to it and forward it to the DOM button it renders
const FancyButton = React.forwardRef((props, ref) = > (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
));

// You can get the DOM button ref directly:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
Copy the code

By doing this, the FancyButton component can get the REF of the DOM node button and access it if necessary, just as if it were using the DOM Button directly.

Steps to resolve

  1. Create a ref variable with React. CreateRef.
  2. React passes ref to the forwardRef as its second argument.
  3. Forward the REF parameter down to button and specify it as a JSX property.
  4. When ref is mounted, ref. Current will point to the Button DOM node.

Considerations for component library maintainers

  • When using the forwardRf, think of it as a new master version because it can cause significantly different behavior.

Forwarding refs in higher-order components

Forwarding refs is useful for higher-order components, let’s take a higher-order component that outputs components to the console as an example

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 logProps component is a higher-order component that is passed to the component it wraps. This higher-order component logs all props passed to the fancyButton component
class FancyButton extends React.Component {
  focus() {
    // ...
  }

  // ...
}

// We export LogProps instead of FancyButton.
// Although it also renders a FancyButton.
export default logProps(FancyButton);
Copy the code
  • Note that refs are not passed because ref is not a prop property, just like key, ref React is handled specially.
  • Using traditional ref passing means that the REF is actually mounted on the LogProps component
import FancyButton from './FancyButton';

const ref = React.createRef();

// The FancyButton component we imported is HOC LogProps.
// Although the render result will be the same,
// But our ref will point to the LogProps instead of the internal FancyButton component!
// This means that we cannot call methods such as ref.current-focus ()
<FancyButton
  label="Click Me"
  handleClick={handleClick}
  ref={ref}
/>;
Copy the code
  • Use the React. ForwardRef to explicitly forward refs to the internal FancyButton component.
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;

      // define the custom prop property "forwardedRef" as ref
      return <Component ref={forwardedRef} {. rest} / >; }}// the react. forwardRef callback has the second argument "ref".
  // We can pass this as a regular prop property to LogProps, such as "forwardedRef"
  // It can then be mounted to a child component wrapped around LogProps.
  return React.forwardRef((props, ref) = > {
    return <LogProps {. props} forwardedRef={ref} />;
  });
}
Copy the code

Display custom names in DevTolls

  • The following component will show up as “ForwardRef” in DevTools
const WrappedComponent = React.forwardRef((props, ref) = > {
  return <LogProps {. props} forwardedRef={ref} />;
});
Copy the code
  • Custom naming case (will show ForwardRef(myFunction))
const WrappedComponent = React.forwardRef(
  function myFunction(props, ref) {
    return <LogProps {. props} forwardedRef={ref} />; });Copy the code
  • Set the function’s displayName property to contain the name of the wrapped component
function logProps(Component) {
  class LogProps extends React.Component {
    // ...
  }

  function forwardRef(props, ref) {
    return <LogProps {. props} forwardedRef={ref} />;
  }

  Provide a more useful display name for this component in DevTools.
  / / such as "ForwardRef (logProps (MyComponent))"
  const name = Component.displayName || Component.name;
  forwardRef.displayName = `logProps(${name}) `;

  return React.forwardRef(forwardRef);
}
Copy the code

The resources

  • Summary of forward ref usage in hooks

Learn React!