React 19 de Cero a Experto / Efectos y ciclo de vida
useRef: la caja que React no observa
useState guarda valores que, al cambiar, repintan la UI. Pero a veces necesitas guardar algo
que NO debe repintar nada: el id de un setInterval, el valor anterior de una variable, o una
referencia directa a un elemento del DOM para enfocarlo. Para eso está useRef: una caja que
persiste entre renders pero que React NO observa —cambiarla no dispara re-render—. Hoy dominas
sus dos usos y, sobre todo, cuándo NO confundirla con el estado.
Una caja con memoria, sin alarma
Uso 1: referenciar un elemento del DOM
El uso más visible: obtener el nodo real del DOM para ordenarle algo que React no expresa
declarativamente, como enfocar un input. Pasas la ref al atributo ref del elemento:
inputRef.current es el elemento <input> real del DOM. React conecta la ref al nodo cuando el
componente se monta (antes, current es null —por eso el ?.—). Enfocar un input es una orden
IMPERATIVA al DOM que React no tiene forma declarativa de expresar, así que es un caso legítimo de
ref. Otros: medir el tamaño de un elemento, hacer scroll a una posición, o integrar una librería
que necesita un nodo del DOM.
Uso 2: un valor mutable que no repinta
El otro uso, menos visible pero clave: guardar un valor que cambia entre renders sin provocar re-render. El caso típico es el id de un temporizador para poder cancelarlo:
function Cronometro() {
const [segundos, setSegundos] = useState(0);
const idRef = useRef<number | null>(null); // guarda el id del intervalo, NO repinta
function iniciar() {
if (idRef.current !== null) return; // ya está corriendo
idRef.current = window.setInterval(() => setSegundos((s) => s + 1), 1000);
}
function parar() {
if (idRef.current !== null) {
clearInterval(idRef.current);
idRef.current = null;
}
}
return (
<>
<p>{segundos}s</p>
<button onClick={iniciar}>Iniciar</button>
<button onClick={parar}>Parar</button>
</>
);
}idRef.current guarda el ticket del setInterval entre renders. Si usáramos una variable normal,
se perdería en cada render; si usáramos useState, cambiarla repintaría sin necesidad (el id no
sale en la UI). useRef es el punto justo: persiste como el estado, pero es invisible para el
render como una variable. Cambiar idRef.current nunca dispara un re-render.
useState o useRef
Necesitas guardar el id que devuelve setInterval para poder cancelarlo luego. ¿useState o useRef, y por qué?
Mini-reto
Usa los dos modos de useRef: 1) un input con una ref y un botón "Limpiar y enfocar" que borre el
texto (estado) y luego haga ref.current?.focus(); 2) un useRef que cuente cuántas veces ha
renderizado el componente (rendersRef.current++ en el cuerpo, y muéstralo) —observa que sube sin
causar renders extra—; 3) explica en un comentario por qué ese contador NO puede ser useState
(causaría un bucle infinito de renders). Siente la diferencia entre "se ve/repinta" y "tras
bambalinas".
Qué sigue
Ya tienes efectos, cleanup y refs. Es hora del caso rey de los efectos en apps reales: CARGAR DATOS de una API. Parece simple, pero esconde una trampa famosa —la "race condition" cuando dos cargas se pisan— y un patrón de tres estados (cargando / error / datos) que vas a repetir mil veces. La próxima lección lo arma bien desde el principio.