# Refs: DOM Access and Mutable Values — React

Source: https://www.skillbyai.com/en/react/h-ref

> 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.

```jsx
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.

**Quiz:** What happens when you change ref.current?

- [ ] The page reloads
- [ ] The component always re-renders
- [ ] React throws an error
- [x] 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.
