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 algunas tareas que todavía necesitan el elemento en sí: enfocar un input después de que se hace clic en un botón, desplazar un mensaje a la vista, medir el ancho que resultó en una caja, reproducir un video. Ninguna de esas se puede expresar como un trozo de JSX, porque cada una actúa sobre un nodo en lugar de describir uno. Una ref es la válvula de escape para ellas. Le da a un componente una forma de aferrarse a un elemento real del DOM y llamar métodos en él directamente.
Asignar 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}>Enfocar</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 aún no hay un nodo 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í, con cada método y propiedad que el navegador le proporciona, así que inputRef.current.focus() dentro del manejador de clic mueve el cursor al campo. React mantiene la asignación actualizada por ti: si el elemento se elimina de la pantalla, React vuelve a establecer current a null.
Desplazar un nodo a la vista
El enfoque es un trabajo. El desplazamiento es el otro que encontrarás temprano, generalmente en algo como un registro de chat que debe saltar al mensaje más nuevo. Ese trabajo se ejecuta después del render, así 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 hacia lo que desplazarse, que 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 apuntado bottomRef.current a ese div final.
Cambiar una ref nunca desencadena un render
El estado y las refs ambas sobreviven de un render al siguiente, y ahí se detiene el parecido. Llamar a un setter de estado le pide a React que renderice el componente nuevamente con el nuevo valor. Asignar a ref.current cambia el valor en silencio, y la pantalla se queda exactamente como estaba.
Ese silencio es el punto completo. Una ref sostiene algo que la UI no muestra: un nodo del DOM, un ID de temporizador, una bandera que tu lógica de renderizado nunca lee. Volver a renderizar cuando uno de esos cambia te costaría un redraw que no podría cambiar un solo píxel.
Tiene una segunda consecuencia que vale la pena retener. Porque nada tiene que ser programado, el valor en ref.current es legible el instante después de que lo asignas. Una variable de estado es fija durante todo un render y solo se mueve en el siguiente, como cubre State. Una ref se comporta como una caja mutable simple que puedes leer y escribir en cualquier momento.
Cuando una ref es el instinto equivocado
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 state, porque la pantalla tiene que cambiar cuando el valor lo hace. Colócalo en una ref y la actualización pasa desapercibida: el valor se mueve, y la UI sigue mostrando lo que renderizó por última vez.
Entrar en el DOM para cambiar lo que se muestra es el mismo instinto un paso más adelante. Establecer node.textContent manualmente, o cambiar una clase directamente, parece funcionar, y luego el siguiente render lo deshace, porque React vuelve a aplicar lo que tu JSX describe. Las refs cubren dos cosas: leer y comandar un nodo, es decir, enfocarlo, desplazarlo, medirlo, reproducirlo, y sostener valores que sobreviven a través de renders sin ser nunca mostrados. Para cualquier cosa que el usuario lee de la pantalla, descríbela en JSX e impulsa con estado.
Nota de versión
Antes de React 19, los componentes de función no podían recibir una prop ref en absoluto. Pasar una a través de un elemento dentro requería envolver el componente en forwardRef. React 19 entrega ref a componentes de función como una prop ordinaria, así que forwardRef es innecesario en código nuevo y está deprecado. Más atrás, los componentes de clase creaban refs con React.createRef() y las leían desde this. El hook useRef cubre ambos roles en componentes de función.
useRef(null), pon el resultado en un elemento con ref={inputRef}, y React rellena inputRef.current con el nodo DOM real una vez que está en la pantalla. Entonces puedes hacer cosas con él, como inputRef.current.focus(). Recurre a una ref cuando necesites enfocar, desplazar, o medir algo. Cualquier cosa que la persona que usa tu app lea de la pantalla pertenece al estado.
Próximo: Hooks, la familia más amplia a la que pertenecen useState, useEffect, y useRef.

