DEV Community

Cover image for 🔹`useRef` – Referencias y valores persistentes (5/8)
Pedro Alvarado
Pedro Alvarado

Posted on

🔹`useRef` – Referencias y valores persistentes (5/8)

📋 Tabla de contenidos

  1. useState – El fundamento del estado
  2. useEffect – Efectos secundarios y ciclo de vida
  3. useContext – Compartir estado sin props
  4. useReducer – Lógica de Estado Compleja y predecible
  5. useRef – Referencias, Almacenamiento y Escape
  6. useMemo y useCallback – Optimizando el Rendimiento
  7. Custom Hooks – Creando Lógica Reutilizable
  8. Errores Comunes y Soluciones

¿Qué es y para qué sirve?

useRef es un hook versátil con dos casos de uso principales que, aunque parezcan diferentes, se basan en el mismo principio: proporcionar un objeto mutable (.current) que persiste durante todo el ciclo de vida del componente sin causar re-renderizados cuando cambia.

  1. Acceder a elementos del DOM: Es la "salida de emergencia" para interactuar directamente con un elemento del DOM, por ejemplo, para manejar el foco, medir su tamaño o integrar librerías de terceros (como D3.js).
  2. Guardar "variables de instancia": Te permite mantener un valor mutable que no necesita disparar una nueva renderización. Es como tener una variable de instancia en un componente de clase. Perfecto para guardar IDs de timers, contadores internos, o cualquier valor que necesites que sobreviva entre renders.

Sintaxis y características clave

const miRef = useRef(valorInicial);
Enter fullscreen mode Exit fullscreen mode
  • useRef devuelve un objeto con una única propiedad: current.
  • miRef.current se inicializa con valorInicial.
  • Puedes cambiar miRef.current directamente: miRef.current = nuevoValor.
  • Importante: Cambiar miRef.current NO dispara un re-renderizado.

Ejemplo práctico detallado: input con foco y contador de renders

Este ejemplo muestra ambos usos de useRef: uno para el DOM y otro para una variable persistente.

import React, { useState, useEffect, useRef } from 'react';

function FocusCounter() {
  const [inputValue, setInputValue] = useState('');

  // Caso de uso 1: Referencia al DOM
  const inputRef = useRef(null);

  // Caso de uso 2: Variable persistente
  const renderCount = useRef(0);

  useEffect(() => {
    // Se ejecuta después de cada render
    renderCount.current = renderCount.current + 1;
  });

  const handleFocus = () => {
    // Accedemos al elemento del DOM directamente
    inputRef.current.focus();
  };

  return (
    <div>
      <h3>Ejemplo de useRef</h3>
      <input
        ref={inputRef} // Conectamos la ref al elemento input
        type="text"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
        placeholder="Escribe algo..."
      />
      <button onClick={handleFocus}>Poner Foco en el Input</button>

      <p>El valor del input es: "{inputValue}"</p>
      <p>Este componente se ha renderizado {renderCount.current} veces.</p>
      <p>(Nota: el contador de renders no causa re-renders por sí mismo)</p>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

En este ejemplo:

  • inputRef nos da acceso directo al <input>, permitiéndonos llamar a .focus().
  • renderCount lleva la cuenta de las renderizaciones. Si usáramos useState para esto, cada actualización del contador causaría otro render, creando un bucle infinito. Con useRef, podemos actualizar el valor sin efectos secundarios.

✅ Buenas practicas y patrones comunes

  1. Cuándo usar useRef vs useState:
    • Usa useState si el cambio en el valor debe ser visible en la UI.
    • Usa useRef si necesitas que un valor persista entre renders pero no quieres que su cambio dispare una nueva renderización.
  2. Guardar el valor previo de props o estado: Un patrón muy útil es combinar useRef y useEffect para "recordar" un valor del render anterior.

    function Componente({ valor }) {
      const valorPrevioRef = useRef();
      useEffect(() => {
        valorPrevioRef.current = valor;
      }, [valor]); // Se actualiza después de que el render se completa
      const valorPrevio = valorPrevioRef.current;
      // ...
    }
    
  3. Manejo de Timers: useRef es ideal para guardar el ID de setInterval o setTimeout para poder limpiarlo después.

🚨 Errores comunes y cómo evitarlos

  • Error: Acceder a ref.current demasiado pronto.

    // MAL ❌
    function MiComponente() {
      const miRef = useRef(null);
      // Aquí, durante el primer render, miRef.current es `null`
      // porque el DOM aún no existe. Esto dará un error.
      miRef.current.focus();
    
      return <input ref={miRef} />;
    }
    
    • Solución: Accede a la ref dentro de un useEffect (que se ejecuta después del renderizado) o en manejadores de eventos (que se ejecutan por interacción del usuario).
    // BIEN ✅
    useEffect(() => {
      // Para el primer render, esto se ejecuta después de que el input existe.
      miRef.current.focus();
    }, []); // Array vacío para que solo se ejecute una vez
    
  • Error: Mostrar el valor de una ref en la UI y esperar que se actualice.

    • Solución: Si necesitas que un cambio se refleje en el render, ese valor debe estar en el estado (useState). Recuerda: useRef no notifica a React cuando cambia.

🚀 Retos prácticos

  1. Reproductor de Video: Crea un componente que muestre un video (<video>). Usa useRef para obtener una referencia al elemento y añade botones para "Play", "Pause" y "Stop".
  2. Cronómetro Preciso: Haz un cronómetro que use setInterval. Guarda el ID del intervalo en una ref para poder detenerlo y reiniciarlo correctamente.
  3. Scroll a un Elemento: Crea una lista larga de elementos. Añade botones al principio que, al hacer clic, hagan scroll automáticamente a un elemento específico en medio de la lista (pista: element.scrollIntoView()).

Top comments (0)