useRef — qué es, el hook de referencias y trabajo con el DOM en React

Autor: IT Sectr Publicado: 2026-07-04 Tiempo de lectura: 9 min

useRef es un hook de React que crea un objeto ref mutable con una propiedad .current, que se conserva entre renderizados del componente. A diferencia de useState, cambiar .current no provoca un re-renderizado, lo que hace que useRef sea ideal para almacenar referencias al DOM, temporizadores y cualquier valor que deba persistir entre renderizados sin volver a dibujar la UI. Según la Documentación de React (2025), el objeto ref se crea una vez durante la vida del componente y no cambia entre renderizados, lo que garantiza la estabilidad de la referencia.

Puntos clave

  • useRef — hook para crear un objeto ref mutable con la propiedad .current.
  • Sin re-renderizado — cambiar .current no provoca un nuevo renderizado del componente.
  • Referencias al DOM — se pasa en la prop ref del elemento para acceso directo al nodo DOM.
  • Almacenamiento de valores — ideal para temporizadores, valores anteriores y cualquier dato entre renderizados.
  • Referencia estable — el objeto ref no cambia entre renderizados, a diferencia de los closures.

Qué es useRef en React

useRef es un hook añadido en React 16.8 que devuelve un objeto ref mutable con una única propiedad .current. El valor inicial se asigna a .current al montar el componente. La principal diferencia con una variable común es que el objeto ref sobrevive a los re-renderizados: en renderizados posteriores React no vuelve a crear el ref, sino que devuelve el mismo objeto.

Internamente, useRef usa el mismo mecanismo que useState y useEffect — una lista enlazada de hooks en el nodo fiber. Sin embargo, a diferencia de useState, React no rastrea los cambios del objeto ref, no encola actualizaciones ni programa re-renderizados. Esto hace que useRef sea un hook extremadamente ligero sin impacto en el rendimiento incluso bajo cambios frecuentes.

Según React Team — Hooks FAQ (2025), useRef es esencialmente una “caja” que almacena un valor. Azúcar sintáctico: const ref = useRef(valorInicial) equivale a const ref = { current: valorInicial } con la diferencia de que React garantiza que el objeto ref será el mismo en cada renderizado. Sin magia — solo una referencia estable gestionada por React.

jsx
import { useRef } from 'react';

function Component() {
    const countRef = useRef(0);
    // countRef.current = 0 inicialmente
    // countRef.current = 5 después de la mutación
    // ¡No hay re-renderizado!
}

Acceso a elementos del DOM con useRef

El caso de uso más común de useRef es obtener acceso directo a un elemento del DOM. React asigna a elementRef.current una referencia al nodo DOM después de montar el componente. Esto es necesario para enfocar un input, medir dimensiones de elementos, integrar con librerías de animación y librerías que no usan el renderizado de React.

React gestiona automáticamente las referencias ref al DOM: al montar asigna el nodo DOM real, al desmontar lo anula (lo pone a null). Esto garantiza que .current siempre tenga el valor actualizado y previene fugas de memoria mediante referencias “colgantes” a elementos eliminados. No se requiere limpieza manual.

jsx
function AutoFocusInput() {
    const inputRef = useRef(null);

    useEffect(() => {
        // Enfocar el input después de montar el componente
        inputRef.current?.focus();
    }, []);

    return <input ref={inputRef} type="text" />;
}

Según React — Refs and DOM documentation (2025), no uses ref para operaciones declarativas — abrir/cerrar modales, gestionar visibilidad. Para esos fines existen state y props. Las refs están diseñadas para operaciones imperativas que no pueden expresarse declarativamente: foco, selección de texto, integración con librerías de terceros.

Almacenamiento de valores mutables sin re-renderizados

El segundo caso de uso más importante es almacenar valores arbitrarios que deben persistir entre renderizados pero cuyos cambios no deben provocar un re-renderizado. Ejemplos típicos: identificadores de temporizadores (setInterval/setTimeout), banderas de cancelación de peticiones, valores anteriores de props para comparación y cualquier dato que no afecte a la salida de la UI.

Esto es especialmente útil en useEffect: useRef almacena el identificador del temporizador y el cleanup lo limpia. Si timerId se almacenara en useState, cada llamada a setTimerId provocaría un re-renderizado innecesario que no se necesita ni para la lógica ni para la UI. useRef resuelve este problema sin sobrecarga y sin renderizados innecesarios.

jsx
function Timer() {
    const intervalRef = useRef(null);

    const start = () => {
        intervalRef.current = setInterval(() => {
            console.log('tick');
        }, 1000);
    };

    const stop = () => {
        clearInterval(intervalRef.current);
    };

    useEffect(() => stop, []);

    return <><button onClick={start}>Start</button><button onClick={stop}>Stop</button></>;
}

useRef vs useState: cuándo usar cada uno

El dilema principal — elegir entre useRef y useState — se resuelve con una pregunta: “¿Se necesita un re-renderizado cuando cambia el valor?”. Si sí — useState. Si no — useRef. useState almacena el estado que afecta a la salida del componente; useRef almacena datos necesarios para la lógica interna pero que no afectan a la UI.

En la práctica, los desarrolladores suelen usar useRef para almacenar callbacks y evitar problemas con closures. Por ejemplo, si useEffect se suscribe a un evento y el callback necesita el estado actual — almacena el callback en useRef. En cada renderizado actualiza ref.current con una nueva función, y el efecto invocará siempre el callback fresco sin necesidad de volver a suscribirse.

CriteriouseRefuseState
Re-renderizadoNo provoca al cambiarProvoca en cada setState
MutaciónDirecta: ref.current = valorMediante setter: setState(valor)
Uso en JSXNo se usa (sin efecto)Se usa en la salida del componente
EjemploTemporizadores, referencias DOM, valores anterioresDatos de formulario, estado UI, banderas

Existe un anti-patrón: usar useRef para datos que se necesitan en JSX pero cuyos cambios no deben provocar re-renderizado. Esto lleva a una desincronización — la UI muestra datos antiguos mientras ref.current ya es nuevo. Si el valor se muestra en la UI — usa useState. Si solo se usa internamente — useRef.

useRef en combinación con useEffect

La combinación useRef + useEffect es el patrón estándar para rastrear valores anteriores de props. Almacena el valor anterior en ref, compáralo con el valor actual en useEffect y toma decisiones basándote en la diferencia. Esto es especialmente útil en animaciones cuando necesitas saber cuál era el valor antes del cambio.

jsx
function PriceDisplay({ price }) {
    const prevPriceRef = useRef(price);

    useEffect(() => {
        const prevPrice = prevPriceRef.current;
        if (price > prevPrice) {
            animateUp();
        } else if (price < prevPrice) {
            animateDown();
        }
        prevPriceRef.current = price;
    }, [price]);

    return <span>${price}</span>;
}

Según React Documentation — Hooks FAQ (2025), este patrón se llama “patrón de valor anterior”. Funciona porque useRef conserva el valor entre renderizados, y useEffect se ejecuta después de confirmar los cambios. Primero se actualiza el DOM con el nuevo price, luego useEffect lo compara con el anterior (aún en ref) y actualiza ref.current al valor actual.

Errores típicos con useRef

El error más frecuente es leer ref.current durante la fase de renderizado para calcular JSX. Dado que cambiar ref.current no provoca un re-renderizado, el componente puede usar un valor desactualizado. Si .current participa en la salida de la UI — usa useState. Si necesitas sincronizar ref y state, usa useEffect para actualizar el state desde ref.

  • Ref en JSX — leer ref.current dentro de una función de renderizado provoca desincronización entre la UI y los datos.
  • Ref como dependencia de useEffect — ref.current no debe estar en el array de dependencias; React no rastrea sus cambios.
  • Olvidar la comprobación de null — al desmontar ref.current se vuelve null; compruébalo antes de acceder.
  • Almacenar funciones en ref — actualiza ref.current en cada renderizado si usas el patrón de callback fresco.
jsx
// ❌ No leas ref.current durante el renderizado para mostrar
function BadComponent() {
    const valRef = useRef(0);
    return <p>{valRef.current}</p>; // NO se actualizará en la mutación
}

// ✅ Usa state para mostrar, ref para lógica
function GoodComponent() {
    const [val, setVal] = useState(0);
    const valRef = useRef(0);
    return <p>{val}</p>;
}

Otro error común es usar useRef como única forma de almacenar estado en un componente cuando los datos realmente afectan a la UI. Los desarrolladores eligen useRef para evitar re-renderizados “innecesarios”, pero terminan con una UI que no se actualiza. El enfoque correcto: usa useState para datos de UI y useRef solo para valores auxiliares que no participan en el renderizado.

Preguntas frecuentes

¿Se puede usar useRef para almacenar el estado anterior?

Sí, es un patrón común — el patrón de estado anterior. Crea un ref y actualízalo en useEffect cada vez que cambie el valor rastreado. Entre el renderizado y el efecto, ref.current contiene el valor anterior, que se puede comparar con el actual. Esto no requiere re-renderizados adicionales y funciona con cualquier tipo de dato.

¿Por qué ref.current es null en el primer renderizado?

React asigna ref.current el valor del nodo DOM solo después de que el elemento se renderiza y se añade al DOM real. Durante el primer renderizado el componente aún no está montado, por lo que ref.current es igual al valor inicial (null). El acceso al DOM mediante ref.current solo es posible en useEffect o en manejadores de eventos que se llaman después del montaje.

¿Cuál es la diferencia entre useRef y createRef?

createRef crea un nuevo objeto ref en cada renderizado — solo debe usarse en componentes de clase. useRef crea el ref una vez y devuelve el mismo objeto en todos los renderizados posteriores. En componentes funcionales usa exclusivamente useRef; createRef provocará la pérdida del valor al re-renderizar porque el objeto se volverá a crear.

¿Se puede pasar useRef a través de props?

Sí, un objeto ref se puede pasar como un prop normal: <Child inputRef={inputRef} />. El componente hijo lo usa mediante inputRef.current. Sin embargo, para pasar un ref directamente al elemento DOM de un componente hijo, usa forwardRef — un HOC que permite reenviar refs a través de props. Sin forwardRef, el ref no se vinculará automáticamente al elemento DOM dentro del componente hijo.

¿Cómo medir el tamaño de un elemento con useRef?

Vincula un ref al elemento, luego en useEffect lee ref.current.getBoundingClientRect() o ref.current.offsetWidth / offsetHeight. Para el seguimiento reactivo de cambios de tamaño, usa ResizeObserver dentro de useEffect: crea un observer, suscríbete a los cambios y actualiza el state con las nuevas dimensiones. No olvides desconectar el observer en el cleanup.

Resumen

  • useRef — hook para crear un objeto ref mutable que se conserva entre renderizados sin provocar re-renderizado.
  • Acceso al DOM — la prop ref se pasa a un elemento para acceso directo al nodo DOM; disponible después del montaje.
  • Almacenamiento de datos — temporizadores, callbacks, banderas y cualquier valor que no afecte a la UI se almacena en ref.
  • useRef vs useState — elige useState si el cambio debe re-renderizar la UI; useRef si no.
  • Patrón de valor anterior — almacena el valor anterior en ref, actualízalo en useEffect para comparación.
  • Ref en JSX — no leas ref.current en la fase de renderizado; esto provoca desincronización entre la UI y los datos.
  • forwardRef — úsalo para pasar un ref a través de props a un componente hijo.

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también