useRef y Manipulación del DOM
Domina las refs de React para acceso al DOM, handles imperativos y valores mutables que persisten entre renderizados.
useRef y Manipulación del DOM
useRef es un hook de React que retorna un objeto ref mutable cuya propiedad .current persiste entre renderizados — sin causar re-renderizados cuando cambia. Es tu escape para operaciones imperativas del DOM, almacenar valores previos y manejar instancias de librerías externas.
🔑 Concepto Clave
useState es para valores que afectan la UI (re-renderiza al cambiar). useRef es para valores que no deben disparar re-renderizados — nodos DOM, timers, valores previos y variables de instancia.
1. Acceder a Elementos del DOM
El caso de uso más común — adjuntar una ref a un elemento JSX e interactuar con el nodo DOM subyacente.
import { useRef, useEffect } from "react";
function InputAutoFocus() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
}, []);
return <input ref={inputRef} placeholder="¡Auto-enfoco!" />;
}
2. Desplazamiento a Elementos
function MensajesChat({ mensajes }) {
const bottomRef = useRef(null);
useEffect(() => {
bottomRef.current?.scrollIntoView({ behavior: "smooth" });
}, [mensajes]);
return (
<div className="overflow-y-auto h-96">
{mensajes.map((msg) => (
<div key={msg.id} className="p-2">{msg.texto}</div>
))}
<div ref={bottomRef} />
</div>
);
}
3. Almacenar Valores Mutables (Timers)
import { useRef, useState, useEffect } from "react";
function Cronometro() {
const [segundos, setSegundos] = useState(0);
const [corriendo, setCorriendo] = useState(false);
const intervalRef = useRef(null);
const iniciar = () => {
if (corriendo) return;
setCorriendo(true);
intervalRef.current = setInterval(() => {
setSegundos((s) => s + 1);
}, 1000);
};
const detener = () => {
clearInterval(intervalRef.current);
intervalRef.current = null;
setCorriendo(false);
};
useEffect(() => {
return () => clearInterval(intervalRef.current);
}, []);
return (
<div>
<p>{segundos}s</p>
<button onClick={iniciar}>Iniciar</button>
<button onClick={detener}>Detener</button>
</div>
);
}
4. Rastrear Valores Anteriores
function useAnterior(valor) {
const ref = useRef();
useEffect(() => {
ref.current = valor;
}, [valor]);
return ref.current;
}
function PrecioDisplay({ precio }) {
const precioAnterior = useAnterior(precio);
const direccion = precio > precioAnterior ? "📈" : precio < precioAnterior ? "📉" : "➡️";
return <span>{direccion} ${precio}</span>;
}
5. forwardRef — Pasar Refs a Componentes Hijos
import { forwardRef, useRef } from "react";
const InputElegante = forwardRef(function InputElegante(props, ref) {
return <input ref={ref} className="border-2 border-purple-500 rounded p-2" {...props} />;
});
function Formulario() {
const inputRef = useRef(null);
return (
<div>
<InputElegante ref={inputRef} placeholder="Escribe aquí..." />
<button onClick={() => inputRef.current.focus()}>Enfocar</button>
</div>
);
}
6. useImperativeHandle — APIs Personalizadas de Ref
import { forwardRef, useRef, useImperativeHandle, useState } from "react";
const Modal = forwardRef(function Modal({ titulo, children }, ref) {
const [abierto, setAbierto] = useState(false);
useImperativeHandle(ref, () => ({
abrir: () => setAbierto(true),
cerrar: () => setAbierto(false),
}));
if (!abierto) return null;
return (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center">
<div className="bg-white rounded p-6">
<h2>{titulo}</h2>
{children}
<button onClick={() => setAbierto(false)}>Cerrar</button>
</div>
</div>
);
});
function App() {
const modalRef = useRef(null);
return (
<div>
<button onClick={() => modalRef.current.abrir()}>Abrir Modal</button>
<Modal ref={modalRef} titulo="Configuración">
<p>Contenido del modal</p>
</Modal>
</div>
);
}
❌ Anti-Patrones
- • No leas/escribas refs durante el renderizado — solo en efectos o manejadores de eventos.
- • No uses refs para reemplazar estado — si cambiar un valor debe actualizar la UI, usa
useState. - • No abuses de refs para manipulación del DOM — prefiere el modelo declarativo de React.