useRef retorna um objeto, { current: initialValue }, que o React mantém durante toda a vida do componente. Mudar ref.current não renderiza nada de novo. Isso torna as refs certas para duas tarefas: guardar um nó do DOM para chamar métodos como focus() nele e lembrar, entre renderizações, um valor que a tela não precisa mostrar.
Passar o objeto ref em ref={inputRef} diz ao React para colocar o nó DOM do <input> em inputRef.current. Troque focus() por select(), digite algo e clique de novo: o texto fica selecionado.
A sintaxe
const myRef = useRef(initialValue);
myRef.current; // read
myRef.current = value; // write, no re-render
initialValueé usado só na primeira renderização. Para refs do DOM ele énull.- O React retorna o mesmo objeto em toda renderização. Só
currentmuda. - Ler e escrever
currenté JavaScript comum. O React não o acompanha.
Um valor que não causa renderizações
O estado serve para o que a tela mostra. Uma ref serve para o que o componente precisa lembrar mas não exibir. Este bloco guarda um contador em uma ref e outro no estado e registra os dois:
Clique em Ref + 1 algumas vezes. O console mostra ref.current subindo, mas a página continua dizendo 0 e nenhuma renderização é registrada. Agora clique em State + 1: o componente renderiza, e o contador da ref na tela salta para o número que a ref já guardava. A ref manteve o valor o tempo todo; o React só não tinha motivo para desenhá-lo.
Refs vs estado
useState | useRef | |
|---|---|---|
| Retorna | [value, setValue] | { current: value } |
| Mudá-lo | Renderiza o componente de novo | Não faz nada visível |
| Quando o novo valor pode ser lido | Na próxima renderização | Na hora |
| Mude com | O setter | Atribuição, ref.current = x |
| Use para | Tudo o que a tela mostra | Ids de timers, nós do DOM, valores anteriores, indicadores |
Se estiver em dúvida, pergunte se o JSX lê o valor. Se lê, ele pertence ao estado.
Guardando o id de um timer
setInterval retorna um id de que você precisa depois para parar o timer. É um bom caso para uma ref: o handler de Stop precisa dele, mas a tela nunca o mostra, e guardá-lo em uma variável comum faria você perdê-lo na próxima renderização.
Substitua useRef(null) por let intervalRef = { current: null }; e experimente Start, espere dois segundos e depois Stop: o timer continua. Cada tique renderiza o componente, o que cria um objeto novo, então o Stop não limpa nada.
Lembrando o valor anterior
Um efeito executa depois da renderização, então ele pode comparar o novo valor com o que está guardado em uma ref e depois salvar o novo para a próxima vez:
Clique em green e depois em blue: o console registra changed from red to green e changed from green to blue. Clicar na cor que já está selecionada não registra nada, porque o efeito só executa quando color muda.
Trabalhando com o DOM
As refs são a forma de chamar APIs do navegador para as quais o React não tem prop: focus(), select(), scrollIntoView(), play() em um vídeo, ou ler um tamanho com getBoundingClientRect().
const lastItemRef = useRef(null);
function scrollToLast() {
lastItemRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
<li ref={lastItemRef}>Last item</li>
ref.current é null durante a primeira renderização, porque o React ainda não criou o nó do DOM, e de novo depois que o elemento é removido. Leia-o em event handlers e efeitos, onde o nó existe. Para medir um elemento e posicionar algo antes de o navegador pintar a tela, leia a ref em useLayoutEffect.
Não leia nem escreva ref.current durante a renderização
O React espera que o corpo do componente seja um cálculo puro: mesmas props e mesmo estado, mesmo JSX. Uma ref muda fora desse modelo, então uma renderização que a lê ou escreve pode mostrar resultados diferentes para as mesmas entradas, e o React pode chamar o corpo mais vezes do que você espera.
function Bad() {
const count = useRef(0);
count.current++; // writing during render
return <p>{count.current}</p>; // reading during render
}
Leia e escreva refs em event handlers e efeitos. A única exceção é a inicialização preguiçosa, em que você preenche a ref uma vez na primeira renderização:
const playerRef = useRef(null);
if (playerRef.current === null) {
playerRef.current = new VideoPlayer(); // runs once
}
(A demonstração do contador com ref acima lê clicks.current no JSX de propósito, para mostrar que a tela não o acompanha. Não copie isso para código real.)
Ref callbacks
Em vez de um objeto ref, você pode passar uma função para ref. O React a chama com o nó do DOM quando o elemento é adicionado. No React 19 a função pode retornar uma limpeza, que o React chama quando o elemento é removido. Isso é útil em uma lista, em que você não pode chamar useRef uma vez por item:
Ao carregar, o console registra uma linha attached por item. Clique em Remove last e só aparece detached Cheese. trackNode é definida fora do componente, então é a mesma função em toda renderização e o React deixa os outros itens em paz. Escreva-a inline como ref={(node) => { ... }} e cada clique desconecta e reconecta todos os itens, porque cada renderização passa uma função nova.
Passando uma ref para o seu próprio componente
No React 19, ref é uma prop comum para componentes de função. O pai cria a ref, e o filho a coloca no elemento do DOM que quer expor:
Antes do React 19 isso precisava de forwardRef, que ainda funciona. A página do forwardRef mostra as duas versões e como expor só alguns métodos com useImperativeHandle.
Erros comuns
Mostrar o valor de uma ref no JSX. A tela não o acompanha. Se o usuário deve ver o valor, use estado.
Usar ref.current como dependência de um efeito. [ref.current] lê o valor durante a renderização e o React não sabe quando ele muda, então o efeito não executa de novo quando muda. Use uma ref callback para reagir a um nó sendo conectado.
Ler uma ref do DOM durante a primeira renderização. Ela é null até o React criar o nó. Leia-a em um efeito ou em um handler.
Criar um objeto novo em vez de usar o hook. const ref = { current: null } no corpo do componente é um objeto novo a cada renderização. Só o useRef dá a você o mesmo objeto toda vez.
Perguntas frequentes
O que o useRef retorna?
Um objeto comum com uma propriedade, current, definida com o valor inicial que você passa. O React retorna o mesmo objeto em toda renderização, então o que você coloca em current continua lá na próxima vez.
Qual é a diferença entre useRef e useState?
Mudar o estado renderiza o componente de novo; mudar ref.current não. Use estado para valores que a tela mostra e uma ref para valores de que só os seus event handlers e efeitos precisam, como o id de um timer ou um nó do DOM.
Por que ref.current é null?
O React define ref.current com o nó do DOM depois de criar o nó, durante o commit. Durante a primeira renderização ele ainda é null, e volta a ser null depois que o elemento é removido. Leia-o em um event handler ou em um efeito.
Mudar ref.current causa uma re-renderização?
Não. O React não sabe que você o mudou. O novo valor só aparece na tela se outra coisa, como uma atualização de estado, renderizar o componente de novo.
Como passo uma ref para um componente filho?
No React 19 um componente de função recebe ref como uma prop comum, então você passa <MyInput ref={inputRef} /> e o filho a coloca no <input> dele. Versões mais antigas precisavam de forwardRef.