ENGINEERING

Managing State with useRef

29 Oct 2025

Sometimes, variables are used internally as part of the app’s mechanism rather than for user interaction. For example, when using setTimeout or setInterval for animations, we may need to store their IDs, or when handling uncontrolled form inputs, we might need to keep references to DOM form elements. In such cases, these values shouldn’t trigger a re-render since they aren’t directly displayed to the user.

How to update state without triggering a re-render

Difference between useState and useRef:
  • When using useState, updating the state triggers a re-render.
  • With useRef, we can update stored values without re-rendering the UI. For example, even when repeatedly clicking a button that updates ref.current, the counter doesn’t visually change — because the component doesn’t re-render.
import { useRef, useState } from "react";

function Counter() {
  const [count, setCount] = useState(1);
  const ref = useRef(1);

  const incCount = () => setCount((c) => c + 1);
  const incRef = () => ref.current++;

  return (
    <div className="App">
      <button onClick={incCount}>State: {count}</button>
      <button onClick={incRef}>Ref: {ref.current}</button>
    </div>
  );
}

export default Counter;
State button:
The State button uses the useState hook, allowing React to manage the counter value as component state. The button’s event handler calls the state updater function setCount to change the counter. Each time the updater is called, React updates the state, triggers a re-render, and injects the latest state value back into the component between renders.

Ref button:
The Ref button uses the useRef hook, allowing React to maintain the counter value in a reference object. Storing the counter value inside this reference object. Even when the value inside the reference changes, it does not trigger a re-render. React keeps the same reference object across renders and assigns it to the ref variable each time.


What is ref.current?

The useRef hook returns an object with a current property. React always returns the same reference object for the same useRef call across renders. On the first render, React sets the current property to the initial value provided to useRef.
const ref1 = useRef("Minimalnomad");
const ref2 = useRef(99);

ref1.current; // "Minimalnomad"
ref2.current: // 99
From the second render onward, React assigns the same reference object to the variable based on the order of useRef calls. By assigning values to the current property of the reference object, we can persist state values across renders.
ref1.current = "Dante";
ref2.current = "1,000";
Not every change deserves a re-render.