Manipulate DOM Elements in External React Components with ForwardRef

John Au-Yeung - Jan 20 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

React is a library for creating front end views. It has a large ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.

To manipulate DOM elements directly with React, we can create a ref, associate it to an element, and then reference that and call its methods to manipulate the DOM item directly.

In this article, we’ll look at how to pass the ref to components outside of its originating component by forwarding refs.

Forwarding refs to DOM components

We can forward refs by creating a ref and then call the forwardRef method to get the ref and then pass it into any place we want.

For example, if we have a Button component that returns a button element, and we want to get the button element from the outside component, we can write the following:

const Button = React.forwardRef((props, ref) => (  
  <button id="custom-button" ref={ref}>  
    {props.children}  
  </button>  
));

class App extends React.Component {  
  constructor(props) {  
    super(props);  
    this.state = {};  
    this.buttonRef = React.createRef();  
  } componentDidMount() {  
    this.buttonRef.current.focus();  
  } render() {  
    return <Button ref={this.buttonRef}>Click Me</Button>;  
  }  
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we have a Button component created by using the React.forwardRef method with a callback passed in.

The callback has a props parameter which has the props that are passed in from the parent component, and the ref object which has the ref that’s passed in from the parent component.

Then we set the ref prop of the button element to the ref from the parameter.

Then we can create a new ref in the App component by calling React.createRef in App ‘s constructor. Then we pass that into the ref prop of Button so that we can access the ForwardRef from the button element.

This means the this.buttonRef is referencing the button element because we passed it into Button , which passes this.buttonRef into the button element via the callback we passed into React.forwardRef .

Then in the App component, we call:

this.buttonRef.current.focus();
Enter fullscreen mode Exit fullscreen mode

in componentDidMount to focus the button in Button from App .

Forwarding refs in Higher-Order Components

We can also forward refs in high-order components. To do this, we write something like the following:

const Button = ({ forwardedRef, children }) => (  
  <button ref={forwardedRef}>{children}</button>  
);

function logProps(Component) {  
  class LogProps extends React.Component {  
    componentDidMount() {  
      console.log(this.props);  
    } 

    render() {  
      const { forwardedRef, ...rest } = this.props;  
      return <Component forwardedRef={forwardedRef} {...rest} />;  
    }  
  } 

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

class App extends React.Component {  
  constructor(props) {  
    super(props);  
    this.buttonRef = React.createRef();  
  } 

  componentDidMount() {  
    this.buttonRef.current.focus();  
  } 

  render() {  
    const ButtonLogProp = logProps(Button);  
    return <ButtonLogProp ref={this.buttonRef}>Click Me</ButtonLogProp>;  
  }  
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we have the logProps higher-order component. The logProps component takes a Component .

logProps return a component with the forwarded refs passed in by calling React.forwardRef with a callback that returns LogProps .

In the App component we pass this.buttonRef as the value to ButtonLogProp .

ButtonLogProp is created by calling logProps with the Button component passed in. This means Component is Button in logProp .

Component, which is Button is then returned in the render method of the LogProp component.

At the end of the logProp higher-order component, we return the component with returned from React.forwardRef . The callback we pass into forwardRef returns the LogProps component with the props including the forwarded ref passed in.

All the props will then reach the Button component, which sets the ref of the button element to the this.buttonRef , which is first passed into logProp , then passed into LogProp , and React.forwardRef returns a component with the forwarded ref passed to the Button component, and then into the button element.

In other words, we passed the ref from App , to logProp , to LogProp , then forwarded with React.forwardRef to Button and then to button .

Conclusion

We can use refs from external components by forwarding refs. This lets us manipulate DOM elements from external components within a component.

Forwarding refs also works with higher-order components. All we have to do is to return the component we want to return in the higher-order component in the React.forwardRef callback with the forwarded ref passed in.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .