useRef: Referencias al DOM

Referencias al DOM y manipulación de datos sin re-renders

Bootcamp ManzDev

¡Bootcamp gratuito!

¡Comiénzalo pulsando aquí!


Hemos mencionado que en el ecosistema de React no se suele hacer referencia al DOM, sino que trabajamos con el JSX de los componentes funcionales y simplemente React dispara re-renders cuando los datos del componente cambia, actualizando los datos y su interfaz visual asociada, que a su vez modifica el DOM de forma automática, sin que el desarrollador tenga que hacerlo manualmente.

En algunas ocasiones tendremos excepciones donde necesitaremos acceder a elementos del DOM manualmente, o simplemente acceder a datos que no queremos que causen re-renders en nuestros componentes al cambiar.

Algunos de esos casos podrían ser:

  • ✨ Poner el foco en un <input>, ejecutando el método focus() de ese elemento del DOM.
  • ✨ Desplazarse a un elemento del DOM, ejecutando el método scrollIntoView() del elemento.
  • ✨ Manipular un <canvas> para obtener el contexto y trabajar con él.
  • ✨ Almacenar un temporizador setInterval() sion que cause re-renders.

Para ello, lo ideal es utilizar el hook useRef.

¿Qué es el hook useRef?

El hook useRef nos permite crear referencias a un elemento del DOM o a un dato que va a cambiar pero que queremos que no dispare re-renders en nuestro componente. Esto es muy habitual cuando necesitamos controlar manualmente un elemento del DOM o cuando queremos que algo, aunque cambie, no dispare re-renders innecesarios.

Veamos un ejemplo donde creamos un componente que envuelve un dialog modal nativo de HTML. Este componente necesita acceder al atributo open para saber si está mostrándose u oculto, y llamar a su método showModal() para mostrarlo de forma modal, por lo que utilizaremos useRef para ello.

A grandes rasgos, las partes importantes son las siguientes:

  • 1️⃣ Primero, creamos dialogRef, la referencia que apuntará al elemento del DOM. De momento es null.
  • 2️⃣ Observa que, en el JSX, usamos el atributo ref para indicar que ese elemento será la referencia.
  • 3️⃣ Luego, en el interior del useEffect, que depende de isOpen, abrimos o cerramos el modal.
import { useRef, useEffect } from "react";

export function ModalDialog({ isOpen, onClose, children }) {
  const dialogRef = useRef(null);

  useEffect(() => {

    if (isOpen) {
      dialogRef.current?.showModal();
    } else {
      dialogRef.current?.close();
    }

  }, [isOpen]);

  return (
    <dialog ref={dialogRef} onClose={onClose}>
      {children}
      <button onClick={onClose}>Cerrar</button>
    </dialog>
  );
}
  /* ... */
  const [isModalOpen, setModalOpen] = useState(true);

  return (
    <div>
      <button onClick={() => setModalOpen(!isModalOpen)}>Toggle</button>
      <ModalDialog isOpen={isModalOpen} onClose={() => setModalOpen(false)}>
        <p>¡Esto es un ejemplo de dialog nativo envuelto con React!</p>
      </ModalDialog>
    </div>
  );
  /* ... */

La primera vez que se ejecute este componente, dialogRef tendrá el valor null porque aún no se ha creado el <dialog>, pero gracias al useEffect, desde que isOpen cambie, se actualizará con el elemento del DOM que queremos. Luego, en la otra pestaña podemos ver que a la hora de utilizarlo, sólo tenemos que crear un estado para definirlo.

En este caso, aunque isOpen esté cambiando, el componente no se re-renderizará, sino que ejecutará la lógica definida sin causar ni disparar un nuevo renderizado.

Ten en cuenta que para acceder a la referencia hemos accedido a la propiedad .current.

Buenas prácticas de useRef

Al utilizar useRef, como con cualquier otro hook o aspecto de React, hay que ser cuidadoso y no utilizar a la ligera este recurso, para no caer en malas prácticas o hábitos. A continuación tienes una serie de recomendaciones:

  • 🟥 Evita usar useRef para estados que deben causar re-renderizados.
  • 🟩 El hook useRef es ideal para referencias a elementos del DOM.
  • 🟩 Usa useRef para almacenar estados mutables (persistentes entre renders) que no afecten a la UI.
  • 🟩 El hook useRef es ideal para almacenar elementos y acceder a sus métodos directamente.
  • 🟩 Usa useRef + useEffect para manejar el ciclo de vida y asegurar que el componente está montado.

¿Quién soy yo?

Soy Manz, vivo en Tenerife (España) y soy streamer partner en Twitch y profesor. Me apasiona el universo de la programación web, el diseño y desarrollo web y la tecnología en general. Aunque soy full-stack, mi pasión es el front-end, la terminal y crear cosas divertidas y locas.

Puedes encontrar más sobre mi en Manz.dev