ReviseAlgo Logo

Hooks

useRef

Master the useRef Hook, covering DOM element references, persistent mutable cache stores, timer management, and useState comparisons.

Last Updated: July 15, 2026 12 min read

1. Learning Objectives

In this lesson, you will learn how to access DOM elements and store mutable values without triggering re-renders. By the end of this topic, you will be able to:

  • Differentiate when to use useState versus useRef.
  • Query and interact with browser DOM elements directly using refs.
  • Store persistent, mutable values that do not trigger re-renders when updated.
  • Manage timers, intervals, and cancellation IDs safely using refs.
  • Avoid common bugs caused by reading refs during rendering.

2. Overview

The useRef Hook returns a mutable object whose .current property persists across renders. Updates to the .current property do *not* trigger a component re-render. useRef is primarily used for: (1) accessing DOM nodes directly, and (2) storing persistent mutable variables (like timer IDs or previous state values).

3. Why This Topic Matters

Managing state without triggering unnecessary re-renders is crucial for performance and DOM synchronization:

  • DOM Interaction: Some tasks (like focusing an input field, scrolling a container to a specific position, or measuring element dimensions) cannot be done declaratively. Refs provide direct access to DOM nodes.
  • Persistent Variables: Storing variables (like interval IDs for animations or timers) inside standard component variables causes them to reset on every render. Storing them in state triggers unnecessary re-renders. Refs solve this by persisting values silently.

4. Real-World Analogy

Think of useRef like **a pocket notebook you carry in a meetings room**:

  • useState (The Presentation Slides): Every time you make a change to the slides, you must re-present the slides to the entire room (re-render the component).
  • useRef (The Pocket Notebook): You write notes in your notebook during the meeting. You can read and write to the notebook silently without interrupting the presentation (no re-renders occur). The notes persist across meetings, but they do not change what is displayed on the screen until you explicitly present them.

5. Core Concepts

Below is a comparison of `useState` and `useRef`:

Feature useState useRef
Trigger re-render on change? Yes. Calling the setter updates the UI. No. Modifying .current updates the value silently.
Persistent across renders? Yes. React preserves values between renders. Yes. The reference object remains identical.
Read during rendering? Yes. Used to describe the UI structure. No. Reading `ref.current` during rendering can cause bugs.
Common Use Case Data displayed on the screen (lists, toggle modes). DOM access (focus, scroll), timers, interval IDs.

6. Syntax & API Reference

This example shows how to use `useRef` to focus an input field:

7. Visual Diagram

This diagram displays how state updates trigger re-renders, while ref updates do not:

8. Live Example — Full Working Code

Below is a complete HTML page demonstrating a timer and auto-focus inputs:

What just happened? The intervalRef object stores the timer ID returned by setInterval. Clicking "Stop" clears the interval using this stored ID. Because intervalRef is a ref, updating its .current property does not trigger re-renders, preventing duplicate timers. The inputElRef links to the input tag, allowing us to focus the input field dynamically using its native DOM API.

9. Interactive Playground

Try It Yourself Challenges:

  1. Add a "Reset" button that clears the timer count and stops the active interval.
  2. Create a ref to track how many times the component has rendered, displaying this number in the console on every render cycle.

10. Common Mistakes

Mistake Why it happens Wrong Correct
Reading or writing ref.current during rendering React expects component rendering to be pure. Reading or writing to ref.current during rendering makes the component impure and can lead to unexpected behavior. return <div>{ref.current}</div> (reading current during rendering) Only read or write to refs inside event handlers or useEffect Hooks.
Expecting UI updates when modifying refs Modifying a ref's .current property does not trigger a re-render. If you use a ref to store data that should update the UI, the screen will not update when the data changes. const countRef = useRef(0);
<button onClick={() => countRef.current++}>
{countRef.current}
</button>
Use useState for data that should trigger UI updates on change.

11. Best Practices

  • Keep render functions pure: Never read or write to `ref.current` during rendering. Only access refs inside event handlers or `useEffect` Hooks.
  • Use for non-visual data: Use refs to store data that does not affect the component's layout (like timer IDs or WebSocket connections).
  • Use state for visual data: Use `useState` for any data that should update the screen when changed.
  • Clean up resources: Always clear any intervals or timers stored in a ref when the component unmounts.

12. Browser Compatibility/Requirements

The useRef Hook is supported in all browsers that run React.

13. Interview Questions

Q1: How does `useRef` differ from `useState`? When should you use each Hook?

Answer: Both preserve values between renders, but they behave differently when updated: (1) `useState` returns a state variable and setter function; updating the state triggers a re-render of the component to update the UI. (2) `useRef` returns a mutable object with a `.current` property; updating `.current` changes the value immediately without triggering a re-render. Use `useState` for data that affects the UI, and `useRef` for DOM access and non-visual data.

Q2: Why is it bad practice to read or write to `ref.current` during the rendering phase of a component?

Answer: Reading or writing to `ref.current` during rendering makes the component impure, breaking React's rendering optimizations. Since React can pause, resume, or re-run rendering blocks, accessing refs during rendering can lead to inconsistent values, rendering bugs, and layout anomalies. Refs should only be accessed inside event handlers or `useEffect` Hooks.

14. Debugging Exercise

Identify the bug in this stopwatch component that causes the timer to reset to 0 whenever the user clicks "Pause":

View Solution

Diagnosis: The timerId variable is declared locally inside the component. When setSeconds triggers a re-render, the Stopwatch function runs again, resetting timerId back to null. When the user clicks "Pause", clearInterval(null) is called, failing to stop the timer.

Fix: Store the timer ID in a ref to persist it across renders:

15. Practice Exercises

Exercise 1: Create a scroll-to-bottom chat window

Build a component named ChatWindow. Use a ref to access the chat message container element. Whenever a new message is added to the chat, scroll the container to the bottom automatically using the element's scrollIntoView() method.

16. Scenario-Based Challenge

The Video Player Controller Widget:

You are designing a custom video player overlay. Clicking the video container should toggle play/pause, and hovering should show progress bars. Explain how you would use useRef to access the HTML5 <video> element directly and control video playback (using .play() and .pause()).

17. Quick Quiz

Q1: Which ref property stores the mutable reference value?

A) ref.value

B) ref.current

C) ref.target

Answer: B — The mutable reference value is stored in the .current property of the ref object returned by useRef.

18. Summary & Key Takeaways

  • The useRef Hook returns a mutable object whose .current property persists across renders.
  • Updates to ref.current do not trigger a component re-render.
  • Use refs to access DOM elements directly or store persistent mutable values.
  • Never read or write to ref.current during the rendering phase.

19. Cheat Sheet

useRef Element Purpose
const ref = useRef(initial); Creates a ref object, initializing its .current property.
ref.current = newValue; Updates the ref value silently, without triggering a re-render.
<div ref={myRef}></div> Links the ref object to the DOM element.
---