Refs e o DOM
React constrói e atualiza o DOM para você. Você descreve como a tela deve parecer, e os elementos reais são responsabilidade do React. Um pequeno número de tarefas ainda precisa do elemento em si: focar um input após um botão ser clicado, rolar uma mensagem para a vista, medir a largura de uma caixa, reproduzir um vídeo. Nenhuma delas pode ser expressa como um pedaço de JSX, porque cada uma age sobre um nó em vez de descrever um. Uma ref é a saída de emergência para elas. Ela dá ao componente um jeito de manter uma referência a um elemento real do DOM e chamar métodos nele diretamente.
Anexando uma ref
Uma ref começa com o hook useRef, e você passa o resultado para um elemento através do 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) retorna um objeto com uma única propriedade, current, contendo o que você passou como valor inicial. Você sempre passa um, e null é o ponto de partida convencional para uma ref do DOM, já que não há nó naquele momento.
Escrever ref={inputRef} é a instrução que o React precisa. Quando o React coloca aquele <input> na tela, ele define inputRef.current como o nó do DOM. A partir daí inputRef.current é o elemento input em si, com todo método e propriedade que o navegador oferece, então inputRef.current.focus() dentro do handler de clique move o cursor para o campo. O React mantém a atribuição atualizada para você: se o elemento é removido da tela, o React define current de volta para null.
Rolando um nó para a vista
Foco é uma tarefa. Rolagem é a outra que você enfrentará cedo, geralmente em algo como um log de chat que deve pular para a mensagem mais recente. Essa tarefa roda após render, então se combina com 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>
)
}A <div> vazia no final existe apenas como algo para rolar até, o que é um truque comum e perfeitamente razoável. O efeito roda sempre que messages muda, e nesse momento o React já colocou as novas mensagens na tela e apontou bottomRef.current para aquela div final.
Alterar uma ref nunca dispara um render
Estado e refs ambos sobrevivem de um render para o próximo, e aí a semelhança termina. Chamar um setter de estado pede ao React para renderizar o componente novamente com o novo valor. Atribuir a ref.current muda o valor silenciosamente, e a tela fica exatamente como estava.
Esse silêncio é o sentido todo. Uma ref mantém algo que a UI não exibe: um nó do DOM, um ID de timer, uma flag que sua lógica de renderização nunca lê. Re-renderizar quando um desses mudar custaria uma redesenho que não poderia mudar um único pixel.
Tem uma segunda consequência que vale a pena reter. Como nada precisa ser agendado, o valor em ref.current é legível no instante depois que você o atribui. Uma variável de estado é fixa durante todo um render e só muda no próximo, como State cobre. Uma ref se comporta como uma caixa mutável comum que você pode ler e escrever em qualquer momento.
Quando uma ref é o instinto errado
A linha divisória é se o valor acaba na tela. Texto em um cabeçalho, um número em um distintivo, se um painel está aberto: tudo isso pertence a state, porque a exibição tem que mudar quando o valor muda. Coloque em uma ref e a atualização passa despercebida: o valor muda, e a UI continua mostrando o que renderizou por último.
Alcançar o DOM para mudar o que é exibido é o mesmo instinto um passo adiante. Definir node.textContent manualmente, ou alternar uma classe diretamente, parece funcionar, e então o próximo render desfaz, porque o React reaplica o que seu JSX descreve. Refs cobrem duas coisas: ler e comandar um nó, ou seja, focar nele, rolar, medir, reproduzir, e manter valores que sobrevivem através de renders sem nunca serem exibidos. Para qualquer coisa que o usuário leia da tela, descreva em JSX e conduza com state.
Nota de versão
Antes do React 19, componentes de função não podiam receber uma prop ref em absoluto. Passar uma através para um elemento dentro exigia envolver o componente em forwardRef. O React 19 entrega ref para componentes de função como uma prop ordinária, então forwardRef é desnecessário em código novo e está deprecado. Mais atrás, componentes de classe criavam refs com React.createRef() e as liam de this. O hook useRef cobre ambas as funções em componentes de função.
useRef(null), coloque o resultado em um elemento com ref={inputRef}, e o React preenche inputRef.current com o nó DOM real uma vez que está na tela. Então você pode fazer coisas com ele, como inputRef.current.focus(). Recorra a uma ref quando você precisa focar, rolar, ou medir algo. Qualquer coisa que a pessoa usando seu app leia da tela pertence a state. Próximo: Hooks, a família mais ampla à qual useState, useEffect, e useRef pertencem.

