El hook useRef
Introducción
useRef es un hook en React que te permite crear una referencia mutable que puede usarse para acceder directamente a un elemento DOM o almacenar un valor que persiste entre renderizaciones sin causar renderizados adicionales.
1. Qué es useRef? #
-
Referencia a elementos DOM:
Se usa comúnmente para acceder a nodos del DOM de manera directa, como inputs o botones. -
Valores persistentes:
También se puede usar para almacenar valores que no deben disparar renderizaciones cuando cambian (a diferencia deuseState).
2. Sintaxis #
1const ref = useRef(valorInicial);
ref.current: Contiene el valor referenciado.valorInicial: Opcional, el valor inicial de la referencia.
3. Caso 1: Acceso a Elementos DOM #
Ejemplo: Foco en un Input #
1import React, { useRef } from "react"; 2 3function FocoInput() { 4 const inputRef = useRef(null); 5 6 const manejarFoco = () => { 7 inputRef.current.focus(); // Enfocar el input 8 }; 9 10 return ( 11 <div> 12 <input type="text" ref={inputRef} placeholder="Escribe algo..." /> 13 <button onClick={manejarFoco}>Enfocar Input</button> 14 </div> 15 ); 16} 17 18export default FocoInput;
Explicación #
useRef: Crea una referencia para el input.inputRef.current: Proporciona acceso al nodo DOM del input.focus(): Enfoca el input al hacer clic en el botón.
4. Caso 2: Valores Persistentes entre Renderizados #
useRef se puede usar para guardar valores que no deberían reiniciarse en cada render, como un contador.
Ejemplo: Contador Sin Renderizado #
1import React, { useRef, useState } from "react"; 2 3function ContadorConRef() { 4 const contador = useRef(0); 5 const [estado, setEstado] = useState(false); // Estado para simular un render 6 7 const incrementar = () => { 8 contador.current += 1; // Incrementar el valor 9 console.log(`Contador actual: ${contador.current}`); 10 }; 11 12 return ( 13 <div> 14 <p>El contador actual es: {contador.current}</p> 15 <button onClick={incrementar}>Incrementar</button> 16 <button onClick={() => setEstado(!estado)}>Forzar Renderizado</button> 17 </div> 18 ); 19} 20 21export default ContadorConRef;
Explicación #
contador.current: Persiste su valor entre renderizados.- Sin re-renderizado: Actualizar
contador.currentno causa que el componente se renderice. - Estado adicional: El botón "Forzar Renderizado" se usa para demostrar que el valor de
contador.currentno se pierde.
5. Diferencia Entre useState y useRef #
| Característica | useState | useRef |
|---|---|---|
| Renderiza al cambiar | Sí | No |
| Persistencia de valores | Entre renderizados | Entre renderizados |
| Uso principal | Manejar datos que afectan la UI | Acceso a nodos DOM o datos que no afectan la UI |
6. Caso 3: Contar Renderizados #
Ejemplo: Contador de Renderizados #
1import React, { useRef, useEffect, useState } from "react"; 2 3function ContadorDeRenderizados() { 4 const renderCount = useRef(0); 5 const [contador, setContador] = useState(0); 6 7 useEffect(() => { 8 renderCount.current += 1; 9 }); 10 11 return ( 12 <div> 13 <p>El componente se ha renderizado: {renderCount.current} veces</p> 14 <button onClick={() => setContador(contador + 1)}>Incrementar</button> 15 </div> 16 ); 17} 18 19export default ContadorDeRenderizados;
Explicación #
renderCount.current: Se incrementa en cada renderización.- Persistencia: El valor se mantiene entre renderizados sin afectar la UI.
7. Caso 4: Almacenar Valores Previos #
Puedes usar useRef para guardar el valor anterior de una variable.
Ejemplo: Valor Previo #
1import React, { useRef, useEffect, useState } from "react"; 2 3function ValorPrevio() { 4 const [contador, setContador] = useState(0); 5 const valorPrevio = useRef(null); 6 7 useEffect(() => { 8 valorPrevio.current = contador; 9 }, [contador]); 10 11 return ( 12 <div> 13 <p>Valor actual: {contador}</p> 14 <p>Valor previo: {valorPrevio.current}</p> 15 <button onClick={() => setContador(contador + 1)}>Incrementar</button> 16 </div> 17 ); 18} 19 20export default ValorPrevio;
Explicación #
valorPrevio.current: Guarda el valor anterior decontador.useEffect: Actualiza el valor previo cada vez quecontadorcambia.
8. Caso 5: Integración con Bibliotecas Externas #
Si necesitas interactuar con elementos DOM mediante bibliotecas externas (como animaciones o mapas), useRef es ideal.
Ejemplo: Animación con GSAP #
1import React, { useRef, useEffect } from "react"; 2import { gsap } from "gsap"; 3 4function AnimacionConRef() { 5 const cajaRef = useRef(null); 6 7 useEffect(() => { 8 gsap.to(cajaRef.current, { x: 200, duration: 2 }); 9 }, []); 10 11 return <div ref={cajaRef} style={cajaEstilo}>¡Muevo con GSAP!</div>; 12} 13 14const cajaEstilo = { 15 width: "100px", 16 height: "100px", 17 backgroundColor: "blue", 18 color: "white", 19 display: "flex", 20 justifyContent: "center", 21 alignItems: "center", 22}; 23 24export default AnimacionConRef;
Explicación #
cajaRef.current: Apunta al nodo DOM.- GSAP: Realiza la animación directamente en el elemento referenciado.
9. Consideraciones Importantes #
-
No Causa Renderizaciones:
Actualizar un valor enuseRefno actualiza la UI. Para eso, usauseState. -
Acceso al DOM:
Úsalo para tareas relacionadas con nodos DOM, como enfocar inputs, medir tamaños o manejar integraciones con bibliotecas externas. -
Valores Persistentes:
Ideal para guardar datos entre renderizaciones sin causar re-renderizados.
10. Cuándo Usar ref en Formularios en Lugar de state #
En React, los formularios generalmente se manejan con estado controlado (state), lo que significa que el valor de los inputs se sincroniza con el estado del componente. Sin embargo, hay casos en los que puede ser mejor usar referencias (ref), que permiten acceder directamente al valor del DOM sin actualizar el estado.
11. Cuándo Usar ref en Lugar de state #
Casos Comunes: #
-
Formularios simples con datos que no necesitan validación o manipulación previa:
Si solo necesitas leer el valor una vez al enviar el formulario,refpuede ser más eficiente. -
Optimización del rendimiento:
Si tienes formularios complejos con muchos campos y no quieres que cada cambio dispare una renderización, es mejor usarref. -
Integración con bibliotecas externas:
Cuando necesitas interactuar directamente con un elemento DOM, como un componente de terceros que no es compatible con React.
12. Ejemplo: Usando ref en un Formulario #
Supongamos que tenemos un formulario que solo captura el nombre de usuario al enviarlo, pero no necesitamos que los cambios en el input disparen re-renderizaciones.
Código #
1import React, { useRef } from "react"; 2 3function FormularioConRef() { 4 const inputRef = useRef(null); 5 6 const manejarEnvio = (e) => { 7 e.preventDefault(); 8 const valor = inputRef.current.value; 9 alert(`El nombre ingresado es: ${valor}`); 10 inputRef.current.value = ""; // Limpia el campo 11 }; 12 13 return ( 14 <form onSubmit={manejarEnvio}> 15 <label> 16 Nombre: 17 <input type="text" ref={inputRef} placeholder="Escribe tu nombre" /> 18 </label> 19 <button type="submit">Enviar</button> 20 </form> 21 ); 22} 23 24export default FormularioConRef;
Resumen del tema
Conceptos clave #
- Objeto Ref Mutable:
const ref = useRef(valorInicial)devuelve{ current: valorInicial }, cuya propiedad.currentpersiste durante todo el ciclo de vida del componente. - Sin Disparo de Render: modificar
ref.currentno provoca un re-renderizado del componente (a diferencia deuseState). - Acceso Imperativo al DOM: vincular elementos mediante el atributo
ref={miRef}para enfocar (.focus()), reproducir medios o medir dimensiones (getBoundingClientRect). - Almacenamiento de Datos Transversales: ideal para guardar IDs de timers (
setInterval), valores previos de estado o banderas de renderizado. - Formularios No Controlados: lectura de valores directamente del DOM en el momento del submit sin sincronización continua por estado.
Qué debes recordar #
useRef retiene referencias mutables (ref.current) entre renderizados sin disparar re-renders y permite el acceso directo a nodos del DOM.