skip to content
Andrei Calazans

Using React Refs

/ 2 min read

  1. The Problem
  2. When do you use refs?
  3. Refs With Classes
  4. Refs With Functional Components
  5. Conclusion

The Problem

There is an often forgotten problem with the usage of Refs in React - The functional components are not referenceable.

When to use refs?

There are cases where you might need to call a method of a component. In a TextInput component, you might have a focus method that focuses and opens up the keyboard. On tv Apps handling the currently focused item on the screen requires heavy usage of ref components. The useRef was also created with the intention of being a replacement for class fields where we usually stored non-reactive data that lived as long as the component existed, e.g., a listener.

Refs With Classes

The following class component can be referenced entirely without any extra mechanism:

class CompA extends React.Component {
  someCall = () => 12;
  render() {
    return <h2>Hello CompA</h2>
  }
}

We can log CompA’s reference <CompA ref={this.compA} /> without any problems:

The result is { current: CompA }

Refs With Functional Components

If we have the following functional component

function CompB() {
  return <h2>Comp B</h2>;
}

By passing a ref to this component as: <CompB ref={refB} />

The result will be { current: undefined }

To fix this issue you need to wrap the component with a the forwardRef api
const CompB = React.forwardRef((props, ref) => {
  return <h2 ref={ref}>Comp B</h2>;
});

This approach now allows you to reference the functional component then pass the ref down to another referenceable element. However, this still does not fix the issue which is providing a ref to the actual CompB component. To solve this later problem we need to use a second api, the useImperativeHandle.

const CompB = React.forwardRef((props, ref) => {
  const [isVisible, setVisible] = useState(false);
  useImperativeHandle(ref, () => ({
    // Add whatever methods you want to make available here
    toggle: () => {
      setVisible(isVisible => !isVisible);
    }
  }));
  return isVisible ?  <h2>Comp B</h2> : null;
});

Now when we reference CompB by doing <CompB ref={refToCompB} /> we will get the following object:

{ current: { toggle: function } }

Conclusion

It is a bit unfortunate how to improve something we end up making other things worse. Nevertheless, I believe the React Core team is making an unconscious point with the reference API since they do not prioritize it and seem to prefer that we don’t have to resort to imperativeness when dealing with Components.