पाठ 19 / 25

Refs: DOM Access and Mutable Values

An escape hatch that does not re-render.

useRef for nodes and values

useRef(initial) returns an object whose .current property persists across renders. Two uses: pointing at a DOM node (via the ref attribute) to focus, measure or integrate with non-React code; and storing mutable values (timer ids, previous values, counters) that should not trigger re-renders. Changing ref.current does not update the screen, so do not read refs during rendering for display; use state for anything shown in the UI.

Focusing an input and counting without re-rendering, run

I ran this with React 19.3 and react-dom 19.3 on Node.js 22, compiling JSX with esbuild 0.28. Browser-style examples use jsdom 29 as a fake DOM (imported from a small setup-dom.mjs file) and act() to apply updates. An effect focuses the input via its ref. Three clicks increment a ref counter without re-rendering (still 1 render, label "show 0"); a state update then re-renders and the label shows 3.

import "./setup-dom.mjs";   // test setup: jsdom provides a browser-like DOM in Node
import { useRef, useState, useEffect, act } from "react";
import { createRoot } from "react-dom/client";

let renders = 0;
function Search() {
  const inputRef = useRef(null);          // points at a DOM node
  const clicks = useRef(0);               // mutable value that does not trigger renders
  const [, force] = useState(0);
  renders++;
  useEffect(() => { inputRef.current.focus(); }, []);
  return <>
    <input ref={inputRef} />
    <button id="track" onClick={() => { clicks.current++; }}>track</button>
    <button id="show" onClick={() => force(x => x + 1)}>show {clicks.current}</button>
  </>;
}
const root = createRoot(document.getElementById("root"));
await act(() => root.render(<Search />));
console.log("focused element:", document.activeElement.tagName);
for (let i = 0; i < 3; i++) await act(() => document.getElementById("track").click());
console.log("renders after 3 tracked clicks:", renders, "| label:", document.getElementById("show").textContent);
await act(() => document.getElementById("show").click());
console.log("after re-render label:", document.getElementById("show").textContent);

Output:

focused element: INPUT
renders after 3 tracked clicks: 1 | label: show 0
after re-render label: show 3

Refs for things the UI does not show

If a value appears on screen, it belongs in state; refs are for behind-the-scenes values and DOM handles.

त्वरित जाँच: What happens when you change ref.current?

  • The page reloads
  • The component always re-renders
  • React throws an error
  • The value changes but the component does not re-render
Answer

The value changes but the component does not re-render — Refs are not reactive.