React

refs, DOM and state: who owns what

useRef keeps a value without re-rendering and state re-renders the component: the choice depends on who has to see the change.

Key points

  • If the value appears in the markup it is state, if you read it only in an effect or handler it is a ref.
  • Changing a ref does not re-render the component and notifies nobody: no effect runs.
  • A ref read or written during render breaks the model, because React may discard that render.

State owns whatever the render draws, a ref owns whatever the behaviour needs: a DOM node, a timer, the last value seen. Changing a ref redraws nothing.

The rule in one line

If the value appears in the markup, it is state. If you read it only inside an effect or an event handler, it is a ref. When in doubt ask who has to notice the change: if the answer is the user, you need state.

The runnable case

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

export function CampoRicerca() {
  const input = useRef(null);
  const tentativi = useRef(0);
  const [testo, setTesto] = useState("");

  useEffect(() => {
    input.current?.focus();
  }, []);

  function invia(e) {
    e.preventDefault();
    tentativi.current += 1;
  }

  return (
    
       setTesto(e.target.value)}
      />
      Cerca
    
  );
}

The field is controlled by state, the node and the counter live in refs. The counter never reaches the screen: if it did, it would become state and the component would redraw on every submit.

Refs and server components

Refs need a client component: they point at a browser node, which does not exist on the server. A component with useRef and no client directive is rejected at build time, not at runtime.

Measure instead of guessing

To read width or position, use a callback ref with ResizeObserver, not a ref read during render. A measured value is browser data, so it belongs in an effect, not in the body of the component.

Live records in the catalogue

The input category collects the fields in the catalogue, with a chat input that manages focus and height; a block example is input-10.

The limitation

A ref notifies nobody: two components cannot synchronise on it without state to wake them. Mutating it during render is also invisible to React, which in concurrent rendering may discard the computation and leave the write behind.

Where to continue

The client and server boundary is in a hook imported in a server component; effects starting twice are in useEffect twice in development; how to read the docs is in reading the React documentation. The comparison with Vue is in Vue 3 vs React.

Frequently asked questions

Why does changing a ref not update the page?

Because React does not watch refs. Only state and props start a render, so a value in a ref changes without the interface knowing.

Can I use a ref instead of state for a counter?

Yes if the counter never reaches the screen and only serves the logic. As soon as it must be visible or change the markup, you need state.

What is a ref on a DOM element for?

For calling methods React does not expose, such as focus, select or scrollIntoView. Reading the value of a controlled field needs only state.

Does a ref survive re-renders?

Yes, the object is the same for the lifetime of the component. It changes only when the component unmounts and is recreated, for example with a different key.

Why is the ref null on the first render?

Because React fills it after creating the node. Read it in an effect or a handler, not in the body of the component.