See Our Public Workshops:
Did you know that the term "ref" in useRef()
doesn't stand for "reference to the DOM", it more stands for "reference to an object". Sometimes we make use of useRef()
without attaching it to the DOM at all.
Broaden your idea of what "state" is
A ref object is this mutable thing you can keep around and it keeps its value between re-renders. I'll show you the pattern below but the main point here is that it's long been known as an alternative to useState
. Advanced React devs know that the term "state" is broad and that all your state doesn't have to be wrapped up in useState
and some global state library. You can use the URL as state, or you can use derived state. Either way, the term "state" just means any value that changes over time in your app.
Before we get into the pattern, I just want to show you this is a "blessed" feature in the React docs and not some weird hack on refs. The old legacy docs had a callout that read something like "You might be familiar with refs for accessing the DOM, but did you know..."
useRef() is useful for more than the ref attribute. It’s handy for keeping any mutable value around. - Legacy React Docs
The pattern is so important that in the new docs for useRef, they spend half the page talking about the mechanics of refs and mutable ref concepts, then half way down the page they finally say "Oh yea, you can use these for accessing the DOM too" - not a real quote but isn't it funny that accessing the DOM with refs is such a late topic on the page.
React now describes refs as:
When you want a component to "remember" some information, but you don’t want that information to trigger new renders, you can use a ref. - React Docs
Mutable Refs
In this component, every time we get a re-render, the x
object will be a brand new object that is not ===
equal to the one in the first render. We can prove this by passing it into the dependency array of useEffect
and you would see that the effect runs after every render because its comparing the old x
to the new one. If this is confusing, be sure to read our other post about immutability and equality in React.
function MyComp() {const myRef = useRef(123)console.log(myRef) // { current: 123 }const x = {}useEffect(() => {// when does this run}, [x])}
We could say that x
isn't "stable" which is a React way of saying it changes on every render. However, the myRef
object that comes from useRef
is memoized which means useRef
will always give you the same object back, which makes it "stable".
The argument you assign in useRef
is assigned to the current
property. In React 19 it's no longer an optional argument so many will use null
to create a similar behavior to previous versions of React.
By assigning this value, you now have an object you could just mutate after things like events or after renders (in useEffect
):
function MyComp() {const myRef = useRef(0)function onClick() {myRef.current++}}
Mutating the ref doesn't cause a re-render. This is the tradeoff between "refs as state" vs useState
. If you want to set state and cause a re-render then by all means just use useState
. But, you might find yourself in a situation where you want a value persisted over time but setting it doesn't cause a re-render. Refs are great for that.
Examples
So now for examples right? Well no. Many real-world examples are nuanced and I think the docs to a decent job of basic examples. I just wanted to introduce the idea to you incase you see this crazy pattern in your code at work, it might not be your co-worker just doing some off-the-wall thing, it's a real pattern to employ when needed.
Instead of having comments here in our blog, we've tweeted about this post so you can comment there if you wish.
View on Twitter