What is the useRef hook? How do you use it? When should you use useRef vs useState? What caveats should you be aware of when using useRef?
This hook is often a tricky one to get right but can be very powerful. Read on to learn how to useRef like a pro!
Table of Contents
What is React’s useRef hook?
useRef is one of the standard hooks provided by React. It will return an object that you can use during the whole lifecycle of the component.
The main use case for the useRef hook is to access a DOM child directly. I’ll show exactly how to do that in another section.
Although accessing the DOM is the main use case, it doesn’t mean it’s the only one! useRef can also be very useful to hold a mutable value across different renders of your component.
For example, it’s often quite handy when using external libraries that weren’t made with React in mind.
You can initialize a new ref inside a component with the following code:
// create a ref
const yourRef = useRef();
You can optionally initialize it with a default value by passing it as an argument to the useRef hook:
// create a ref
const yourRef = useRef('hello world');
Tip: useRef is a hook, and as such can only be used in functional components! To use refs in class components, you have createRef instead. I briefly show how to use createRef further down below. To Learn more about the difference between functional and class-based components in React check out this guide.
How to use React useRef?
Once created, you can get and set the value of the ref by accessing the .current property of the object, like so:
// create a ref
const exampleRef = useRef();
// set the ref value
exampleRef.current = "Hello World";
// access the ref value:
// this prints "Hello World" to the console
console.log(exampleRef.current);
To access a DOM element, you create a ref, assign it to the DOM element you want to target using its ref attribute, then you can use it!
For example, say you want to get the height in pixels of a DOM element. To do this, you have to access the offsetHeight property of the DOM element. But how to get access to the DOM element? With a ref of course!
import { useEffect, useRef } from "react";
export default function App() {
// create a ref
const divElement = useRef();
// trigger on the first render of the component
useEffect(() => {
// get the height of the div element
console.log(
"The height of the div is: ", divElement.current.offsetHeight
);
}, []);
return (
<div ref={divElement}>
<h1>Learn about useRef!</h1>
</div>
);
}
Tip: In the above example I’ve used useEffect to only call divElement.current.offsetHeight once the component was first rendered. Indeed, at first, the ref is undefined, and it’s only once the component renders and the div is created that it holds its DOM element value!
As you can see refs can be quite tricky to use, and you have to keep a few things in mind.
Caveats of using useRef
Some important things to keep in mind when using useRef are:
A ref changing value doesn’t trigger a re-render
This one is often a tricky one, and trips a lot of developers! It is the opposite behavior of what happens when using useState.
For example, the following code has a bug! Can you spot where it is?
import { useRef } from "react";
export default function App() {
// create a ref
const counter = useRef(0);
// increase the counter by one
const handleIncreaseCounter = () => {
counter.current = counter.current + 1;
};
return (
<div>
<h1>Learn about useRef!</h1>
<h2>Value: {counter.current}</h2>
<button onClick={handleIncreaseCounter}>
Increase counter
</button>
</div>
);
}
Have you spotted it? If so, good job! The issue is that clicking the button increases the variable counter as expected, but it doesn’t trigger a re-render so we see nothing on the screen!
Tip: to learn how to use click handlers like the handleIncreaseCounter, check out this blog post!
It is useless to add a ref to a dependency array
Adding a ref to a dependency array (for example the one of a useEffect hook) will not trigger the callback! This is also a very common error.
For example, in the following example, you can click on the button all you want and it won’t print anything to the console!
import { useEffect, useRef } from "react";
export default function App() {
// create a ref
const counter = useRef(0);
// increase the counter by one
const handleIncreaseCounter = () => {
counter.curent = counter.current + 1;
};
useEffect(() => {
console.log("counter changed to: ", counter.current);
}, [counter]);
return (
<div>
<h1>Learn about useRef!</h1>
<h2>Value: {counter.current}</h2>
<button onClick={handleIncreaseCounter}>
Increase counter
</button>
</div>
);
}
To fix both of these bugs, you should use useState instead of useRef:
import { useEffect, useState } from "react";
export default function App() {
// create a counter
const [counter, setCounter] = useState(0);
// increase the counter by one
const handleIncreaseCounter = () => {
setCounter(counter + 1);
};
useEffect(() => {
console.log("counter changed to: ", counter);
}, [counter]);
return (
<div>
<h1>Learn about useRef!</h1>
<h2>Value: {counter}</h2>
<button onClick={handleIncreaseCounter}>
Increase counter
</button>
</div>
);
}
Now it works!
Tip: in the above example, I’ve used the callback form of useState. You can learn more about it in this blog post.
When to avoid using useRef?
Most of the time, if you want to persist state across re-renders of your components you should be using useState instead of useRef. Defaulting to useState, and then only using useRef if you have a specific reason to do so is a good rule to code by!
Check out more info on useState on this blog post!
createRef vs useRef
useRef is the hook to create refs in functional components, but you can also use refs in your class components! The way you do it is by using the createRef function.
The usage is very similar to useRef:
import { Component, createRef } from ‘react’;
class YourComponent extends Component {
constructor(props) {
super(props);
this.yourRef = createRef();
}
render() {
return <div ref={this.yourRef} />;
}
}
Wrap Up
I hope you now have a good understanding of what refs are and how to use them!
Here is some good further reading if you want more material on refs and useRef:
- useRef API reference
- React’s guide on Refs and the DOM. The guide uses class components but you can translate it pretty easily to functional components and useRef instead!
💬 Leave a comment