TechLead

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.