Menu

Hook useRef no React: refs do DOM e valores que persistem

useRef dá a um componente uma caixa, { current }, que mantém o conteúdo entre renderizações sem causar uma renderização quando você o muda. Use-a para alcançar um nó do DOM (foco, rolagem, medidas) ou para guardar o id de um timer ou qualquer valor que a tela não mostra.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

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ó current muda.
  • 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

useStateuseRef
Retorna[value, setValue]{ current: value }
Mudá-loRenderiza o componente de novoNão faz nada visível
Quando o novo valor pode ser lidoNa próxima renderizaçãoNa hora
Mude comO setterAtribuição, ref.current = x
Use paraTudo o que a tela mostraIds 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.

Ilustração das linguagens de programação do Coddy

Aprenda a programar com o Coddy

COMEÇAR