useState vs useRef

May 20, 2025

O que é useState?

O useState é hook do react, que de forma uma simples funciona como a memória do seu componente para os dados que, quando alterados , devem fazer o componente renderizar novamente.

Funciona assim:

  1. Você declara uma variável de estado e uma função pra atualizar ela
  2. Quando você usar essa função para mudar algum valor, o React vai ver e redesenhar esse “pedaço da tela”

Um exemplo básico:

import React, { useState } from "react";

function Counting() {
  const [counter, setCounter] = useState(0);

  const handleClick = () => {
    setCounter(counter + 1); // Quando isso roda, o número na tela muda.
  };

  return (
    <div>
      <p>Cliques: {counter}</p>
      <button onClick={handleClick}>Clique Aqui</button>
    </div>
  );
}

Se você só mudar uma variável normal do JS, o React nem fica sabendo, a tela vai continua estática. O useState é o que trás a reatividade pro componente conectando a mudança de dados com a atualização da interface.

Uma coisa que eu demorei a entender é: Quando você atualiza algum estado, tipo setCounter(counter + 1), o counter não muda instantaneamente. O React agenda uma atualização. Se em algum código você precisar do valor anterior pra calcular o novo, eu acho que o ideial seria usar: setCounter(prevCount => prevCount + 1)

O que é useRef?

O useRef é hook do react que também memoriza dados entre renderizações, mas a pricipal diferença é que mudar um ref Não faz o componente renderizar de novo

Funciona assim:

  1. Você declara uma ref com useRef(), passando um valor inicial se quiser (tipo useRef(0) pra um contador interno, ou useRef(null) se for pra “grudar” num elemento da tela
  2. “Pra pegar ou mudar o valor que tá dentro da ref, você sempre vai usar a propriedade .current dela (ex: ref.current = 'novo valor' ou console.log(ref.current))

Importante: ao usar useRef para referenciar um elemento DOM (ex: inputRef), a propriedade inputRef.current só será preenchida com o nó DOM após a montagem e renderização inicial do componente. Por isso, o acesso a inputRef.current para manipulação DOM frequentemente ocorre dentro de um useEffect

Um exemplo básico:

import React, { useRef, useEffect } from "react";

function Input() {
  const inputRef = useRef(null);

  useEffect(() => {
    // Quero que o input já venha focado quando a tela carregar
    inputRef.current.focus();
  }, []);

  const showValue = () => {
    alert(`You typed: ${inputRef.current.value}`); // Pega o valor diretamente do DOM via ref
  };

  return (
    <div>
      <input ref={inputRef} type="text" placeholder="Auto-focus here!" />{" "}
      {/* Linka a ref ao input */}
      <button onClick={showValue}>Show Value</button>
    </div>
  );
}

useState vs useRef`

Quando Usar?useStateuseRef
Preciso que a tela atualize quando o valor mudar?SIM! É o pilar dele.NÃO. A mudança é silenciosa pra UI.
Quero mexer em um elemento DOM diretamente?Não é pra isso.SIM! Focar input, tocar vídeo, etc.
Preciso guardar um valor que não afeta a UI?Dá, mas vai re-renderizar à toa.SIM! Perfeito para IDs de timers, instâncias, contadores “invisíveis”.

Um exemplo usando os dois

import React, { useState, useRef, useEffect } from "react";

function Stopwatch() {
  const [seconds, setSeconds] = useState(0);
  const intervalRef = useRef(null);

  const startTimer = () => {
    if (intervalRef.current) return; // Se já tem ID, tá rodando

    intervalRef.current = setInterval(() => {
      setSeconds((s) => s + 1); // Atualiza o estado, que atualiza a tela
    }, 1000);
  };

  const pauseTimer = () => {
    clearInterval(intervalRef.current);
    intervalRef.current = null; // Limpa o ID pra indicar que não está rodando
  };

  // Importante: limpar o intervalo se o componente sumir da tela. Evita memory leaks.
  useEffect(() => {
    // Esta função de retorno é a 'cleanup function' do useEffect
    return () => clearInterval(intervalRef.current);
  }, []);

  return (
    <div>
      <p>Time: {seconds}s</p>
      <button onClick={startTimer}>Start</button>
      <button onClick={pauseTimer}>Pause</button>
    </div>
  );
}

Sem o useRef pro ID, se a gente colocasse ele num estado, cada setInterval pode causar re-renderizações desnecessárias só pra guardar um número que não aparece.

Minha conclusão

No fim das contas, pra mim ficou assim:

  • useState: É para UI dinâmica. Quer ver a coisa mudar na tela? Tenta usar ele.

  • useRef: É pra usar/modificar DOM e pra guardar valores “por baixo dos panos” sem fazer a tela piscar.