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 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.
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!
}
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.
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.
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.
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></>;
}
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.
| Criterio | useRef | useState |
|---|---|---|
| Re-renderizado | No provoca al cambiar | Provoca en cada setState |
| Mutación | Directa: ref.current = valor | Mediante setter: setState(valor) |
| Uso en JSX | No se usa (sin efecto) | Se usa en la salida del componente |
| Ejemplo | Temporizadores, referencias DOM, valores anteriores | Datos 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.
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.
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.
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.
// ❌ 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
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.
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.
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.
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.
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
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.
Lea también