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 }
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.