useRef

⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 2 min

useRef

useRef is a React Hook that lets you reference a value that’s not needed for rendering. It provides a way to persistently store mutable values across renders without causing a component to re-render when the value changes.

Syntax

import { useRef } from 'react';

const ref = useRef(initialValue);

useRef returns an object with a single property:

  • current: Initially set to the initialValue you passed. You can later read or write to this property.

Primary Use Case 1: Manipulating the DOM

The most common use case for useRef is to directly access a DOM element to call imperative APIs (like .focus(), .play(), or .scrollIntoView()).

To do this, you create a ref and pass it to the ref attribute of a JSX element. React will assign the DOM node to ref.current.

function TextInputWithFocusButton() {
  // 1. Create the ref
  const inputEl = useRef(null);
  
  const onButtonClick = () => {
    // 3. Use the ref to interact with the DOM node directly
    inputEl.current.focus();
  };

  return (
    <>
      {/* 2. Attach the ref to the element */}
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

Primary Use Case 2: Storing Mutable Values

You can also use a ref to store any value that you want to persist between renders, but that doesn’t affect the visual output of the component.

For example, tracking a timer ID so you can clear it later:

function Timer() {
  const [count, setCount] = useState(0);
  // Store the interval ID in a ref so it doesn't trigger re-renders when set
  const timerIdRef = useRef(null);

  const start = () => {
    timerIdRef.current = setInterval(() => setCount(c => c + 1), 1000);
  };

  const stop = () => {
    clearInterval(timerIdRef.current);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={start}>Start</button>
      <button onClick={stop}>Stop</button>
    </div>
  );
}

Differences between useRef and useState

FeatureuseRefuseState
Does it trigger a re-render?No. Mutating .current does not re-render.Yes. Calling the state setter triggers a re-render.
Is the value mutable?Yes. You can modify .current directly.No. You must use the state setter function.
When to use?Values that don’t affect the UI (timers, DOM nodes).Values that directly affect what is rendered on screen.

Interview Questions

Q: What are the two primary use cases for useRef?
A:

  1. Accessing native DOM elements directly (e.g., calling .focus() on an input).
  2. Storing mutable values (like timers, intervals, or previous state) that persist across renders without triggering a re-render when they are updated.

Q: If you update a useRef value (ref.current = 10), will the component update on the screen?
A: No. Changing a ref does not trigger a re-render. This makes it perfect for tracking background data, but terrible for tracking data that needs to be displayed in the UI.