All about React's createRef method
React refs are a useful and powerful tool that we can use to get even more control over the elements in our application.
In this article, you’ll learn what you need to know about refs and how to use them in your class and function components.
Do you want to see a working example? Click here to skip the explanation.
Or have a look at the codepen here.
What are refs in React?
“Ref” is short for reference, and it’s a React feature that allows us to store a component or DOM element of our application in a variable and access it programmatically.
We can use refs to access DOM elements and React components.
When (not) to use refs
The official React documentation states that the following use cases are a good fit for using refs:
- Managing focus, text selection, or media playback.
- Triggering imperative animations.
- Integrating with third-party DOM libraries.
They also say that we should avoid using refs for anything that can be done declaratively.
Declaratively in React means we should use the JSX syntax whenever we can, instead of using JavaScript to update our application.
Leaving the DOM updates to React makes sense. After all, React is designed to update the elements on our websites as efficiently as possible.
Sometimes though, React doesn’t provide the functionality we need, in which case we need to fall back to plain JavaScript.
If we were to get a DOM element in Vanilla JavaScript, we’d use functions like document.getElementById
or document.getElementsByClassName
.
const inputElement = document.getElementById('input-element-id');
inputElement.focus();
But React offers a more convenient way to access elements in our application: Refs.
Every class component and HTML element in React accepts the ref
prop, which we can use to store a reference to this element.
How to use createRef
That was enough of theory for now. Let’s have a look at how we can use refs in our applications.
For this tutorial, I created a small codepen that allows you to enter your name and focus the input when you click on the button.
The HTML input element doesn’t accept any focus
prop that we could use to focus it. However, the HTML node does expose the focus
function.
We somehow need to get access to the element in our code so we can call this function. Instead using getElementById
, we’ll use React’s refs.
First, we need to create a new variable in our class where we will store the reference. Mine has the creative name inputRef
.
constructor(props) {
super(props);
this.inputRef = React.createRef();
// ...
}
We initialize the variable with createRef
, which allows us to simply pass the variable to the element that we want to reference.
<input
ref={this.inputRef}
value={this.state.name}
onChange={this.handleInputChange}
/>
That’s everything we need to do to get access to the input element.
The inputRef
variable now contains a reference to the input
element.
If we were to inspect the variable at this point, it would look something like this.
▶ Object
▶ current: input
▶ __proto__: Object
We can focus the input element by calling the focus function in the onClick
handler function.
<button
onClick={() => this.inputRef.current?.focus()}
>
Focus
</button>
In case you are wondering, adding the question mark is called the optional chaining, first introduced by TypeScript. It allows us to safely access current
without worrying about it being undefined or null.
Full example
Putting everything together, the code of that component looks like the following:
class App extends React.Component {
constructor(props) {
super(props);
this.inputRef = React.createRef();
this.state = {
name: '',
};
}
handleInputChange = (e) => {
e.preventDefault();
this.setState({ name: e.target.value });
}
render() {
return (
<>
<h1>Hi {this.state.name || 'there'}! ??</h1>
<p>
What's your name?
</p>
<input
ref={this.inputRef}
value={this.state.name}
onChange={this.handleInputChange}
/>
<button
onClick={() => this.inputRef?.current?.focus()}
>
Focus
</button>
</>
);
}
}
This code is also available on codepen.
Referencing class components
With refs, we can also reference other React components. When we do this, instead of receiving a DOM node, current
will contain the mounted instance of that component.
Note that this doesn’t work with function components because, unlike class components, they don’t have instances.
When dealing with class components, the syntax is the same as with DOM elements:
<MyComponent ref={this.myComponentRef} />
You don’t need to change anything in MyComponent
.
Referencing function components using forwardRef
I know that just one moment ago I said that you can’t reference function components. However, if you really need to do this, you can forward the ref to a DOM element.
For that, we use the forwardRef
function.
const MyInputComponent = React.forwardRef((props, ref) => (
<input ref={ref} />
));
This doesn’t allow us to reference the functional component itself but one of the native elements that it renders.
With this modification, we can use the ref
prop as we did with class components and HTML elements.
const MyInputComponent = React.forwardRef((props, ref) => {
return (
<input
ref={ref}
value={props.name}
onChange={props.onChange}
/>
);
});
In App
we can pass down the ref
property like before.
class App extends React.Component {
//...
render() {
return (
<>
<MyInputComponent
ref={this.inputRef}
name={this.state.name}
onChange={this.handleChange}
/>
</>
);
}
}
I created a seperate codepen for this example.
Other ways to reference elements
Above I only explained one way of creating refs because I believe it’s the easiest way of doing so.
You can do this differently though, and you might encounter these ways of creating refs in some code bases.
Callback refs
The ref
prop, besides accepting objects created with createRef
, also accepts a callback.
<input
ref={(e) => this.inputRef = e}
value={this.state.name}
onChange={this.handleInputChange}
/>
Note that we don’t access the current
field in the callback because the function directly receives the element.
Because of this, we don’t need to initialze the variable with createRef
. We can set it to null
in the constructor:
constructor(props) {
super(props);
this.inputRef = null;
this.state = {
name: '',
};
}
When accessing the element in our code, we can again omit current
as inputRef
already contains the element:
<button
onClick={() => this.inputRef?.focus()}
>
Focus
</button>
The callback will be called with the element once the component mounts, and it will be called with null
before the component unmounts.
React guarantees that it will be up to date before componentDidMount
or componentDidUpdate
are executed.
How to use useRef
The createRef
function we know from class components also has its counterpart for functional components: The useRef
hook.
We first need to initialize the variable.
const inputRef = useRef(null);
We then pass this value to the ref
prop:
<input
ref={inputRef}
value={name}
onChange={handleChange}
/>
The usage of this hook is very similar to createRef. However, there are some other use-cases that I discuss in my article about React’s useRef hook.
Observability for Production React Apps
While you’re testing the createRef method, and how to interact directly with DOM references, debugging your React apps in production may be challenging and time consuming. OpenReplay is an open-source session replay stack for developers. It helps you replay everything your users do and shows how your app behaves and renders for every issue. It’s like having your browser’s inspector open while looking over your user’s shoulder.
Open Source Session Replay
OpenReplay is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data.
Start enjoying your debugging experience - start using OpenReplay for free.
Conclusion
React’s refs are a useful tool for situations where we can’t do things the ”React way“.
I hope that with this article, you have a good overview of how and when to use them.