← Back to Chapters

React useRef() Hook

⚛️ React useRef() Hook

Hook DOM Access Mutable Values

⚡ Quick Overview

The useRef() Hook creates a reference object that can store a mutable value which:

  • Does not cause the component to re-render when it changes.
  • Can point to a DOM element (like an input or a div).
  • Can store any value that should persist across renders (timers, previous values, etc.).

? Key Concepts

  • Ref object: Returned by useRef() and has a single property: current.
  • No re-renders: Updating ref.current will not re-render the component.
  • DOM access: Attach the ref to a JSX element via the ref attribute.
  • Persistent storage: Ideal for storing values across renders (interval IDs, previous state, etc.).

? Syntax & Basics

The basic syntax of useRef() looks like this:

? View Code Example
// Create a ref with an optional initial value
const myRef = useRef(initialValue);

// Access or update the stored value
myRef.current = 42;
console.log(myRef.current);
  • myRef.current → The actual value stored (DOM node, number, object, etc.).
  • initialValue → Usually null for DOM elements or a primitive like 0.

? Common Use Cases

  • Focusing an input field automatically or on button click.
  • Storing render counters or custom metrics.
  • Tracking the previous value of a state variable.
  • Storing interval / timeout IDs for timers.

? Example 1: Accessing DOM Elements

Use useRef to directly access a DOM element and call methods like focus().

? View Code Example
// Focus the input when the button is clicked
import React, { useRef } from "react";

function InputFocus() {
  const inputRef = useRef(null);

  const handleFocus = () => {
    inputRef.current.focus();
  };

  return (
    <div className="text-center">
      <input
        ref={inputRef}
        type="text"
        placeholder="Type here..."
        className="form-control mb-2"
      />
      <button className="btn btn-primary" onClick={handleFocus}>
        Focus Input
      </button>
    </div>
  );
}

export default InputFocus;

? What You’ll See

The page renders an input and a button. When you click “Focus Input”, the cursor jumps into the text box using inputRef.current.focus().

? Example 2: Storing Mutable Values Between Renders

Here, useRef tracks how many times the component has rendered, without causing extra renders.

? View Code Example
// Track how many times the component has rendered
function RenderCounter() {
  const [count, setCount] = React.useState(0);
  const renderCount = React.useRef(0);

  React.useEffect(() => {
    renderCount.current += 1;
  });

  return (
    <div className="text-center">
      <h4>Count: {count}</h4>
      <p>Rendered {renderCount.current} times</p>
      <button
        className="btn btn-success"
        onClick={() => setCount(count + 1)}
      >
        Increment
      </button>
    </div>
  );
}

? What You’ll See

Each time you click Increment, the count increases and the message updates, showing how many times the component has rendered so far, using renderCount.current.

⚙️ Example 3: Preserving Previous State

Use useRef to remember the previous value of a state variable across renders.

? View Code Example
// Store the previous value of "count"
function PreviousValue() {
  const [count, setCount] = React.useState(0);
  const prevCount = React.useRef();

  React.useEffect(() => {
    prevCount.current = count;
  }, [count]);

  return (
    <div className="text-center">
      <h5>Current: {count}</h5>
      <h6>Previous: {prevCount.current}</h6>
      <button
        className="btn btn-primary"
        onClick={() => setCount(count + 1)}
      >
        Increment
      </button>
    </div>
  );
}

? What You’ll See

The UI shows both the current and previous values of count. Each click updates the current value and shifts the old one into prevCount.current.

? Example 4: Timer with useRef

Store the timer ID in a ref so it persists across renders and can be started or stopped safely.

? View Code Example
// Build a simple timer using a ref for the interval ID
function Timer() {
  const [seconds, setSeconds] = React.useState(0);
  const timerRef = React.useRef(null);

  const startTimer = () => {
    if (!timerRef.current) {
      timerRef.current = setInterval(() => {
        setSeconds((s) => s + 1);
      }, 1000);
    }
  };

  const stopTimer = () => {
    clearInterval(timerRef.current);
    timerRef.current = null;
  };

  return (
    <div className="text-center">
      <h5>Time: {seconds}s</h5>
      <button
        className="btn btn-success me-2"
        onClick={startTimer}
      >
        Start
      </button>
      <button
        className="btn btn-danger"
        onClick={stopTimer}
      >
        Stop
      </button>
    </div>
  );
}

? What You’ll See

Clicking Start begins a timer that increases every second. Clicking Stop clears the interval using the stored timerRef.current value.

? Comparison: useState vs useRef

How useRef differs from useState:

Hook Purpose Triggers Re-render?
useState Stores data that affects rendering ✅ Yes
useRef Stores mutable values or DOM references ❌ No

? Tips & Best Practices

  • Use useRef for values that must persist but shouldn’t re-render the component.
  • Great for accessing DOM elements like inputs, divs, canvases, or video elements.
  • Combine useRef with useEffect to track previous values or manage subscriptions.
  • When referencing DOM nodes, initialize the ref with null (e.g., useRef(null)).
  • Remember: updating ref.current will not trigger a re-render—use useState when you need reactivity.
  • Always access the stored value through .current (for example, inputRef.current).

? Try It Yourself

  1. Create a focusable input field using useRef to control focus when a button is clicked.
  2. Build a timer or stopwatch that uses useRef to store and clear the interval ID.
  3. Track the previous value of a counter with useRef and useEffect.
  4. Update ref.current in a component and confirm that the UI does not re-render automatically.

Goal: Use useRef() to access DOM elements and persist values between renders without triggering re-renders — perfect for timers, inputs, and previous value tracking.