O React oferece duas maneiras de manter os dados vivos dentro de um componente: useState e useRef. À primeira vista, eles parecem semelhantes. Ambos retornam algo que você pode ler, ambos sobrevivem a re-renderizações e ambos permitem que você lembre de um valor de um clique para o próximo. No entanto, se escolher o errado, você acabará com uma tela que se recusa a atualizar ou com um desfile interminável de renderizações desnecessárias. A escolha não é sobre sintaxe. É sobre se o React precisa saber.

O que cada Hook realmente faz

useState é a linha de comunicação oficial do React com um componente. Quando você o chama, recebe de volta um valor e uma função setter. O React rastreia esse valor como parte da identidade do componente. Quando o setter é acionado, o React diz: “Algo mudou” e agenda uma nova renderização para que a tela possa ser atualizada.

useRef, por outro lado, não passa de um objeto JavaScript comum com uma propriedade current. O React promete entregar a você exatamente a mesma referência de objeto em cada renderização. Ele não observa o que está dentro. Mutar someRef.current acontece silenciosamente. O React não reagirá.

Esse silêncio é o ponto principal. Refs são uma válvula de escape, não um substituto para o estado.

A Divisão de Renderização

Se você alterar o estado, o componente é renderizado novamente. Este é o comportamento que a maioria dos iniciantes espera, e é exatamente o que você deseja quando os novos dados devem aparecer na tela. Um contador, um campo de formulário, uma lista de usuários buscada — se o usuário vê, provavelmente pertence ao estado. Todo o fluxo de dados do React é construído em torno da ideia de que as mudanças de estado notificam o renderizador para sincronizar o DOM.

Se você alterar uma ref, nada acontece visualmente. A variável é atualizada imediata e sincronamente, mas o componente não é renderizado novamente. Isso torna as refs ideais para valores que dão suporte ao funcionamento interno do componente sem fazer parte da saída visual. Pense em IDs de timer, snapshots de props anteriores ou handles diretos do DOM. A UI não se importa com o ID do intervalo que controla o autoplay de um carrossel; ela só se importa com qual slide está visível. O ID do intervalo pertence a uma ref.

Quando o Estado é a Ferramenta Certa

Recorra ao useState sempre que um valor fizer parte da interface visual da sua UI.

Campos de entrada são o exemplo óbvio. Se um usuário digita um endereço de e-mail e você precisa validá-lo e mostrar uma mensagem de erro abaixo da caixa, essa string de e-mail precisa de estado. A lógica de validação e o banner de erro dependem do valor mais recente, e o React só sabe que deve atualizar o banner porque o estado acionou uma nova renderização.

Contadores e alternâncias (toggles) são outro item essencial. Um botão que incrementa uma pontuação, uma flag de abrir/fechar um modal, um índice de aba — tudo isso flui através do estado porque a saída de renderização muda junto com o valor. Mesmo valores derivados, como uma lista filtrada que depende de uma string de busca, geralmente começam com estado porque o valor de origem é visível para o usuário.

Há também uma nuance de tempo que vale a pena entender. As atualizações de estado são assíncronas e agrupadas (batched). Se você chamar setCount(count + 1) três vezes dentro de um único manipulador de eventos, o React não renderizará três vezes. Ele as agrupa em uma única atualização. A variável count dentro da sua função em execução também permanece desatualizada até a próxima renderização. Esse agrupamento é um recurso. Ele mantém as aplicações rápidas. Mas isso significa que você não pode esperar que a variável de estado reflita o novo valor logo na linha seguinte.

Quando as Refs Salvam o Dia

Use useRef para a lógica de suporte, não