Refs y el DOM
React construye y actualiza el DOM por ti. Describes cómo debe verse la pantalla, y React se encarga de los elementos reales. Hay una pequeña cantidad de tareas que todavía necesitan el elemento en sí: enfocar un input después de hacer clic en un botón, desplazar un mensaje a la vista, medir el ancho de una caja, reproducir un video. Ninguna de esas cosas se puede expresar como JSX, porque cada una actúa sobre un nodo en lugar de describir uno. Una ref es la puerta de escape para estas tareas. Le da a un componente una forma de mantener una referencia a un elemento del DOM real y llamar métodos en él directamente.
Adjuntar una ref
Una ref comienza con el hook useRef, y pasas el resultado a un elemento a través del atributo ref:
import { useRef } from 'react'
function SearchBar() {
const inputRef = useRef(null)
function handleFocus() {
inputRef.current.focus()
}
return (
<>
<input ref={inputRef} />
<button onClick={handleFocus}>Focus</button>
</>
)
}useRef(null) devuelve un objeto con una sola propiedad, current, que contiene lo que pasaste como valor inicial. Siempre pasas uno, y null es el punto de partida convencional para una ref del DOM, ya que no hay nodo todavía en ese momento.
Escribir ref={inputRef} es la instrucción que React necesita. Cuando React coloca ese <input> en la pantalla, establece inputRef.current al nodo del DOM. A partir de entonces inputRef.current es el elemento input en sí mismo, con cada método y propiedad que el navegador le ofrece, por lo que inputRef.current.focus() dentro del manejador de clic mueve el cursor al campo. React mantiene la asignación actualizada para ti: si el elemento se elimina de la pantalla, React establece current de nuevo en null.
Desplazar un nodo a la vista
Enfocar es una tarea. Desplazarse es la otra que encontrarás rápido, normalmente en algo como un registro de chat que debe saltar al mensaje más reciente. Esa tarea se ejecuta después del renderizado, por lo que se empareja con useEffect:
import { useRef, useEffect } from 'react'
function MessageList({ messages }) {
const bottomRef = useRef(null)
useEffect(() => {
bottomRef.current.scrollIntoView({ behavior: 'smooth' })
}, [messages])
return (
<div>
{messages.map(message => (
<p key={message.id}>{message.text}</p>
))}
<div ref={bottomRef} />
</div>
)
}El <div> vacío al final existe solo como algo a lo que desplazarse, lo cual es un truco común y perfectamente razonable. El efecto se ejecuta cada vez que messages cambia, y para entonces React ya ha colocado los nuevos mensajes en la pantalla y ha apuntado bottomRef.current a ese div final.
Cambiar una ref nunca dispara un renderizado
El estado y las refs ambos persisten de un renderizado al siguiente, y ahí es donde termina el parecido. Llamar a un setter de estado le pide a React que renderice el componente de nuevo con el nuevo valor. Asignar a ref.current cambia el valor silenciosamente, y la pantalla se queda exactamente como estaba.
Ese silencio es el punto completo. Una ref guarda algo que la interfaz de usuario no muestra: un nodo del DOM, un ID de temporizador, una bandera que tu lógica de renderizado nunca lee. Renderizar de nuevo cuando uno de esos cambios te costaría un redibujado que no podría cambiar ni un pixel.
Tiene una segunda consecuencia que vale la pena recordar. Como nada tiene que estar programado, el valor en ref.current es legible al instante después de asignarlo. Una variable de estado está fija durante todo un renderizado y solo se mueve en el siguiente, como explica State. Una ref se comporta como una caja mutable simple que puedes leer y escribir en cualquier momento.
Cuándo una ref es la intuición incorrecta
La línea divisoria es si el valor termina en la pantalla. Texto en un encabezado, un número en una insignia, si un panel está abierto: todo eso pertenece al estado, porque la pantalla tiene que cambiar cuando el valor lo hace. Ponlo en una ref y la actualización pasa desapercibida: el valor se mueve, y la interfaz de usuario sigue mostrando lo que renderizó por última vez.
Alcanzar el DOM para cambiar lo que se muestra es la misma intuición un paso más allá. Establecer node.textContent a mano, o alternar una clase directamente, parece funcionar, y luego el siguiente renderizado lo deshace, porque React vuelve a aplicar lo que tu JSX describe. Las refs cubren dos cosas: leer y controlar un nodo, es decir enfocarlo, desplazarlo, medirlo, reproducirlo, y mantener valores que sobreviven a los renderizados sin ser nunca mostrados. Para cualquier cosa que el usuario lea en la pantalla, descríbela en JSX e impulsa el estado.
Version note
Before React 19, function components could not receive a ref prop at all. Passing one through to an element inside required wrapping the component in forwardRef. React 19 delivers ref to function components as an ordinary prop, so forwardRef is unnecessary in new code and is deprecated. Further back, class components created refs with React.createRef() and read them from this. The useRef hook covers both roles in function components.
useRef(null), coloca el resultado en un elemento con ref={inputRef}, y React completa inputRef.current con el nodo del DOM actual una vez que está en la pantalla. Luego puedes hacer cosas con él, como inputRef.current.focus(). Recurre a una ref cuando necesites enfocar, desplazar o medir algo. Todo lo que la persona que usa tu app lee en la pantalla pertenece al estado. A continuación: Hooks, la familia más amplia a la que useState, useEffect y useRef pertenecen.

