useRef vs. useState - When to Use Each?
7 Mins

As React developers, we save data, update it, or even change our applications' UI daily. So, it is a must to know our day-to-day tools inside out.
Today, we want to start briefly with useState
and useRef
and then dive deeper into their use cases and how they behave differently.
You have used useState
a lot so far. But, have you considered why useState? How does it behave? What does it add to our application? We want to cover these questions and know what happens in our code whenever we use this hook. This is a simple app that uses useState
syntax to store so-called data as the state of the component.
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
Whenever we use useState
we are telling React that this data is special to us. Please, remember it between renders so I may use it. In our code above, we want to remember the isModalOpen
data. The convention is to name the state like this [someValue, setSomeValue]
. Though the name can be anything, make sure you follow the rules.
This hook only takes one argument which is the initial data of your state. It returns two important things in an array (tuple) for us. The first one is the data itself, and the second one is the setter function we need to update the data. For example, this code shows how a modal is shown in the UI.
import { ChangeEvent, useState } from "react";
const UserInformation = () => {
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const openModalHandler = () => {
setIsModalOpen(true);
};
const closeModalHandler = () => {
setIsModalOpen(false);
};
return (
<div className="flex min-h-72 min-w-96 flex-col gap-10 rounded-md bg-white p-4 text-center shadow-md">
<div className="flex items-center justify-between">
<span>Modal is: {`${isModalOpen ? "visible" : "invisible"}`}</span>
<div className="space-x-4">
<button onClick={openModalHandler}>Open</button>
<button onClick={closeModalHandler}>X</button>
</div>
</div>
{/* Modal here */}
{isModalOpen && <Modal />}
</div>
);
};
export default UserInformation;
const Modal = () => {
const [userName, setUserName] = useState<string>("");
const changeUserNameHandler = (e: ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setUserName(value);
};
return (
<div className="flex flex-col gap-4">
<input
className="rounded-md border border-teal-800 px-2 py-1 focus:border-2 focus:border-teal-900 focus:outline-none"
value={userName}
placeholder="Enter your name"
onChange={changeUserNameHandler}
/>
<span>User's name is {userName}</span>
</div>
);
};
The modal shows a user's name that you can fill in the input and see how it gets updated in our application accordingly.

We should only update the state using the setter function. Only in this case, React will update the state, and updating the state will trigger the component to re-render. Now, what happens if we close the modal? Will it remember the state data?
Our data will get reset when we close the modal and open it again you see that the data is an empty string. This is what we should expect and know about React when we make use of useState. React uses a Virtual DOM which is a javascript object representation of the real DOM. It makes the computation of UI differences easier and whenever a change happens - like our modal getting removed from DOM - React creates a new VDOM.
With the help of the reconciliation process and diffing algorithm, it updates the DOM efficiently. If you wish to learn more about it, you can check out my React Behind the Scenes.
Key Takeaways:
- States will get destroyed when the DOM node is removed from the DOM or its position has changed inside the tree.
- To keep our UI updated and synced with our data, we need to store it as a state.
- To update a state, we must use the state setter function to trigger re-render.
States are local to components. It means even if you render multiple instances of the Modal
component, each one will have its own state. Changing any of them won't affect the others.
This is another important reason why it is better to use useState
for data we want React to remember and update UI accordingly. Imagine you had used a normal variable for storing data. Not only, would not it get retained between renders, but also we would encounter mutations.
Though you can have multiple states inside one component, be careful not to overuse it and store everything inside a state since we don't want our component to re-render when every data changes.
There are 3 questions you ask yourself when you want to store data as a state:

Well done for keeping it up down here. It was a brief and good summary of the React useState
. Let's go for useRef.
This hook is somehow similar to useState
. We use this hook when we want Reach to remember some data for us. Wait! Did not we say useState
was supposed to do this job for us? Yes, you are right. But - there is always a but - useRef
will tell React to remember data but it won't trigger a re-render. This is a key point that distinguishes these two hooks.
We can use useRef
and pass initial data to it like this.
const ref = useRef(0);
It returns an object with a property called current which holds the data inside itself.
// ref object will be like this
{
current: 0;
}
Despite states that we must not mutate, we can easily mutate a ref and read and write to it.
Supposing we have a counter. We have a incrementCounterHandler
which whenever invoked, will update our ref. But wait, why don't we see any changes in the UI? The answer is simple. Because refs do not trigger a re-render in React.

We mostly use refs when we want to store data that does not affect the component's UI. For example, we can use refs for:
- Storing timeout IDS
- Storing DOM elements
- Storing objects/data that ous JSX does not rely on
Here, I have already summarized and pulled out key points and takeaways from this article in a way you can refer to it easily whenever you need.
- We use both
useState
anduseRef
when we want React to remember the data. useState
is used when our UI depends on the changes of the data and we need to trigger a re-render!useRef
is used when we do not want to trigger a re-render!- The state of our components is isolated inside them and having multiple instances of the same component won't affect each other.
- React will keep track of the state as long as the component is inside the DOM or in the same position in the UI tree.
And there it is. Henceforth, you will always remember these important points whenever using states and refs. I hope you have found this useful. Thank you for reading. Please share and like if you found this article helpful.
Cover image is from: le le StSaPhoto by Rahul Mishra on Unsplash