React and refs, an eternal love story

Tuesday, Feb 20 2018 in react javascript

As in our previous exercise, we want to get the DOM node for a form component to validate the form with the HTML constraint validation API.

Since React 16.3, we can store references to DOM nodes with React.createRef(). Let’s repeat the exercise with the new API.

We have two components, Form and its parent App. We want to store the validation logic inside App, but we need access to the <form> DOM node inside the Form component to use the constraint validation API.

React.createRef() creates special objects that can store references to React element DOM nodes. We’ll create one of these objects in the App component, then pass it down to the Form component to grab a reference to the <form> DOM node.

In the App component, create an instance variable with React.createRef():

class App extends React.Component {
  state = {
    validated: false
  };

  formRef = React.createRef();
}

Now we can access formRef inside the render() function as this.formRef. Pass this.formRef to the Form component as the innerRef prop:

class App extends React.Component {
  render() {
    return (
        <Form
          innerRef={this.formRef}
        >
      );
    }
}

Just as before, the Form component passes innerRef to its inner form element as a ref prop:

function Form({ innerRef, children, ...otherProps }) {
 return (
    <form noValidate className={formClasses} ref={innerRef} {...otherProps}>
      {children}
    </form>
  );
}

Once React mounts the Form component, thanks to the magic of React.createRef(), formRef.current in App will contain a reference to the <form> element DOM node. Since formRef.current is a <form> DOM node, we can call .checkValidity() to validate the form.

class App extends React.Component {
  onSubmit() {
    this.formRef.current.checkValidity()
  }
}

React.createRef() gives you an object that stores the DOM node automatically, so you don’t need to write a function to do that for you.

So many refs, so little time

To recap, since React 16.3, you can pass either a function or a value created with React.createRef() as the ref prop.

If you pass a function, React will call the function when the component mounts, with the DOM node as an argument, and you can store a reference to the DOM node to access it later.

If you pass an object you created with React.createRef(), you’ll be able to access the DOM node through the current property on that object. That means you have to store the return value of React.createRef() before passing it as a ref prop.

The function ref prop is still more powerful, because React also calls the function when it unmounts the component (in that case, React calls the function with null), so you can perform arbitrary logic on both unmounting and mounting.

Further Reading

Take a look at the RFC for the new ref API to learn more about the motivation behind it. To learn about React fundamentals, read React for Real, of course ;).