All About the React useRef Hook with a Real World Example
React hooks were introduced in React v16.8, and made functional components incredibly powerful. With React hooks like useCallback
, useEffect
, and others (all of which I’ve written about), developers were equipped with the tools to turn React functional components into the building blocks of all React applications.
In this article, we’ll be looking at the React hook useRef
and how we can use it to upgrade our React functional components.
What is useRef?
What exactly is the useRef
hook?
const refContainer = useRef(initialValue);
useRef
takes in a single function argument, initialValue
, and returns a mutable reference object whose .current
value is initialized to the passed argument. useRef
is essentially a “box” whose current
key holds a mutable value.
useRef
serves a couple main purposes:
- Access DOM elements
- Persist values across successive renders
Accessing DOM Elements
useRef
is most commonly used when accessing React DOM elements. For example, if you’re trying to access an input
element after it’s been mounted to the DOM, instead of using the traditional document.getElementById
or any other document
method to access to element (like you would in vanilla JS), you can use a useRef
hook. Lets take a look at an example of this below:
In this code, we are creating a component Form
and returning a form
element. We invoke useRef
with a null function argument, so useRef
will return an object { current: null }
assigned to inputRef
. Since we pass this to the ref
parameter of input
, we are assigning inputRef
to the input
DOM element. Now we can access and manipulate input
using the inputRef
variable, in this case setting focus on the input
on initial load using the useEffect
hook.
Persist Values Across Successive
useRef
isn’t just for DOM refs, but it’s a generic container that can hold anything, similar to an instance property on a class. There are situations where you may want to persist values across renders instead of recreating them on each render. Instead of storing these values in states which may cause additional unnecessary rendering, you can put them in useRef
‘s current property. For example, lets look at this Timer
component.
Here, you can see we’re using useRef
to keep track of an interval ID that’s persisted across renders, allowing us to create a timer on first load and clear it on last unmount.
There is one thing to keep in mind, useRef
doesn’t notify you when it changes Mutating the .current
property won’t cause a re-render. If you want to run code when stuff is being mutated, it’s probably better to use a useCallback
instead.
Real World Example
When learning about the useRef
hook, I initially was a little confused on where I could actually use this in React. However, I’ve found that useRef
can be used to create a custom comparison instead of the shallow one that useEffect
does by default.
Lets look at this Todo component with a point value that randomly increase.
Our expectation is that the useEffect
hook on line 4 will only run when we increase the number of points the todo
is worth. However, if we run this code and click the button a couple times, we’ll see that regardless of if the points
value changes, the useEffect
hook will always run!
This is because useEffect
does a shallow comparison by default and todo
is an object!
Lets try using the lodash deepEqual
operator in our useEffect
to see if we can fix this bug:
Now we run into another problem, how do we access previousTodo
? This is where useRef
comes to the rescue, we can save previousTodo
to the useRef
current value to access on each subsequent re-render!
Now we’re keeping track of the previous todo
by saving it to the .current
property of the useRef
hook since it survives after each render. Another useEffect
hook is used to update the previousTodoRef.current
value after each re-render. Finally, we can now extract the previousTodo
value to do a deep compare to ensure that we only log when the todo
actually changes!
If we want to go one step further, we can actually extract all this logic to a new hook called usePrevious
:
Finally, lets apply this custom usePrevious
hook to our Todo
component:
That’s pretty sweet, we’ve now created a working Todo component that keeps track of previous values to compare against!
Conclusion
The useRef
hook is a powerful hook for two main purposes: accessing DOM elements and persisting values across re-renders. We saw how we can use useRef
in both of these cases to write cleaner and better React code! Looking for more resources on the useRef
hook? Check out the React documentation for more information!
Keep in Touch
There’s a lot of content out there and I appreciate you reading mine. I’m a undergraduate student at UC Berkeley in the MET program and a young entrepreneur. I write about software development, startups, and failure (something I’m quite adept at). You can signup for my newsletter here or check out what I’m working on at my website.
Feel free to reach out and connect with me on Linkedin or Twitter, I love hearing from people who read my articles :)