GadgetForge

GadgetForge

React Hooks - useRef


useRef is one of React's hooks. It is mainly used for:

  • directly accessing DOM elements, or
  • storing values that are not related to rendering

Unlike useState, changing the value of a ref does not cause the component to re-render.


Definition and Basic Syntax of useRef

useRef lets you persist a value across renders without triggering re-renders when the value changes, and it is commonly used to reference DOM elements.

 javascript
import { useRef } from "react";

// Most common pattern: initialize with null for DOM refs
const ref = useRef(initialValue);

Why initialize with null?

  • DOM elements are null before they are rendered → safe access
  • Clearly communicates "this will eventually hold a DOM node"
  • Follows React's official convention for refs
  • Makes the code more predictable and self-documenting

Common Use Cases with Examples

1. Accessing DOM Elements

The most frequent use case: focusing an input, measuring size, scrolling, etc.

 javascript
import { useRef } from "react";

function FocusInput() {
  const inputRef = useRef < HTMLInputElement > null;

  const handleFocus = () => {
    console.log("Before focus:", inputRef.current);
    inputRef.current?.focus();
    console.log("After focus:", inputRef.current);
  };

  return (
    <div>
      <input ref={inputRef} type="text" placeholder="I'll get focus!" />
      <button onClick={handleFocus}>Focus</button>
    </div>
  );
}

Console output example after clicking the button:

 javascript
Before focus: <input type="text" placeholder="I'll get focus!">
After focus: <input type="text" placeholder="I'll get focus!" autofocus>

2. Storing values that should not trigger re-renders

Very useful for:

  • previous values
  • timers / intervals
  • last submitted value
  • render counters
  • flags (e.g. "isMounted")

Example: Prevent duplicate form submissions

 javascript
import { useRef, useState } from "react";

function PreventDuplicateSubmission() {
  const [inputValue, setInputValue] = useState("");
  const lastSubmittedValue = useRef < string > "";

  const handleSubmit = () => {
    if (inputValue === lastSubmittedValue.current) {
      alert("Duplicate values cannot be submitted.");
      return;
    }

    // Submit the new value
    lastSubmittedValue.current = inputValue;
    console.log("Submitted value:", inputValue);
    setInputValue(""); // clear input
  };

  return (
    <div>
      <input
        type="text"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
        placeholder="Enter a value"
      />
      <button onClick={handleSubmit}>Submit</button>
      <p>Last submitted value: {lastSubmittedValue.current}</p>
    </div>
  );
}

Why "" instead of null here?
Using null would require extra null checks:

 javascript
if (lastSubmittedValue.current === null || inputValue === lastSubmittedValue.current)

Starting with an empty string is often cleaner in this specific case.

3. Tracking previous state / previous value

A very common pattern:

 javascript
import { useEffect, useRef, useState } from "react";

function PreviousValueTracker() {
  const [count, setCount] = useState(0);
  const prevCount = useRef(0);

  useEffect(() => {
    prevCount.current = count; // store current value as "previous" for next render
  }, [count]);

  return (
    <div>
      <p>Current value: {count}</p>
      <p>Previous value: {prevCount.current}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

Key Characteristics of useRef

FeatureDescription
No re-render on changeChanging .current does not cause a re-render (unlike useState)
DOM accessAttach to elements via ref={myRef}myRef.current gives the DOM node
Persists across rendersValue survives re-renders and lives as long as the component stays mounted
MutableYou can freely mutate .current — that's its main purpose

useRef vs useState – Quick Comparison

FeatureuseRefuseState
Initial valueuseRef(initialValue)useState(initialValue)
Triggers re-render?NoYes
Main purposeDOM refs, non-rendering values, timersUI state, values that affect rendering
Typical usage.current mutation, previous valuesState + setState, controlled components
Persists valueYes (across renders)Yes (but changing it causes re-render)


Related Posts in Series
Collapse