Cheat sheet de React Hooks: cada hook com um exemplo ao vivo

Os React hooks são funções como useState, useEffect e useRef que dão aos function components estado, efeitos colaterais e refs. Esta cheat sheet cobre todos os hooks nativos do React 19.3 com a função de cada um em uma linha, um exemplo ao vivo e o erro mais comum, mais 12 custom hooks para copiar.

Todos os hooks nativos em resumo

No momento em que escrevemos (outubro de 2026), a versão mais recente do React é a 19.3. A tabela lista todos os hooks da referência oficial de hooks do React, mais os três hooks do React 19 documentados em páginas próprias (use, useOptimistic e o hook do React DOM useFormStatus). Hooks marcados com React 18 funcionam no React 18 e no 19. Hooks marcados com React 19 não existem no React 18. Cada hook do React 18 abaixo tem um exemplo ao vivo em que você pode clicar; os do React 19 aparecem só como código, porque esta página roda em React 18.

Mostrando todos os 20
HookGrupoUse paraDesde
useStateEstadoguardar um valor que renderiza o componente de novo quando mudaReact 18
useReducerEstadogerenciar estado com muitas atualizações relacionadas por meio de uma única função reducerReact 18
useContextContextoler um valor do provider mais próximo acimaReact 18
useRefRefguardar um valor mutável ou um nó do DOM que não dispara renderizaçõesReact 18
useImperativeHandleRefescolher quais métodos um componente pai pode chamar por meio de uma refReact 18
useEffectEfeitosincronizar com algo fora do React depois da pinturaReact 18
useLayoutEffectEfeitomedir o layout antes de o navegador pintarReact 18
useInsertionEffectEfeitoinserir regras de estilo antes dos layout effects rodarem (bibliotecas CSS-in-JS)React 18
useEffectEventEfeitoler as props mais recentes dentro de um effect sem executá-lo de novoReact 19.2
useMemoDesempenhoguardar em cache um cálculo caro entre renderizaçõesReact 18
useCallbackDesempenhoguardar uma função em cache para que filhos memoizados não renderizem de novoReact 18
useTransitionDesempenhomarcar uma atualização de estado como não urgente e receber um indicador de pendênciaReact 18
useDeferredValueDesempenhodeixar uma parte lenta da interface ficar para trás durante a digitação rápidaReact 18
useIdOutrosgerar IDs únicos e estáveis para labels e atributos ARIAReact 18
useSyncExternalStoreOutrosassinar uma store que vive fora do ReactReact 18
useDebugValueOutrosrotular um custom hook no React DevToolsReact 18
useActionStateOutrosacompanhar o resultado e o estado pendente de uma action de formulárioReact 19
useOptimisticOutrosmostrar o resultado esperado de uma action antes de ela terminarReact 19
useAPIler uma promise ou um context, mesmo dentro de condiçõesReact 19
useFormStatusReact DOMler o estado pendente do formulário paiReact 19

Os hooks seguem duas regras: chame-os só no nível superior de um componente ou custom hook (nunca dentro de loops, condições ou funções aninhadas), e chame-os só a partir de function components do React ou de outros hooks. O use é a única exceção à primeira regra: ele pode ser chamado dentro de blocos if e de loops. O pacote eslint-plugin-react-hooks verifica as duas regras para você.

Hooks de estado

useState

O que faz: Guarda um valor para este componente. Chamar o setter agenda uma nova renderização com o novo valor.

import { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <>
      <button onClick={() => setCount((c) => c - 1)}>-1</button>
      <span>{count}</span>
      <button onClick={() => setCount((c) => c + 1)}>+1</button>
    </>
  );
}

Erro comum: Escrever setCount(count + 1) três vezes em um clique e esperar +3. As três chamadas leem o mesmo count desta renderização, então você ganha +1. Passe uma função de atualização, setCount(c => c + 1), quando o próximo valor depender do anterior. O botão "+3 (updater)" acima faz exatamente isso. Um segundo clássico: mutar um objeto ou array no lugar (user.name = "x"; setUser(user)). O React compara por referência, vê o mesmo objeto e pula a renderização. Crie um objeto novo: setUser({ ...user, name: "x" }).

useReducer

O que faz: Move a lógica de atualização para uma única função pura que recebe o estado atual e uma action e retorna o próximo estado. Útil quando vários valores mudam juntos.

type Action = { type: "add"; text: string } | { type: "toggle"; id: number };

function reducer(state: Todo[], action: Action): Todo[] {
  switch (action.type) {
    case "add":
      return [...state, { id: Date.now(), text: action.text, done: false }];
    case "toggle":
      return state.map((t) => (t.id === action.id ? { ...t, done: !t.done } : t));
  }
}

const [todos, dispatch] = useReducer(reducer, []);
dispatch({ type: "add", text: "Ship it" });

Erro comum: Fazer efeitos colaterais dentro do reducer (buscar dados, gravar no localStorage, gerar IDs aleatórios a partir de um timer em que você confia depois). No Strict Mode de desenvolvimento, o React chama os reducers duas vezes para expor código impuro. Mantenha o reducer puro e coloque os efeitos colaterais em event handlers ou effects.

Hooks de contexto

useContext

O que faz: Lê o valor do provider correspondente mais próximo acima deste componente e renderiza de novo quando esse valor muda.

const ThemeContext = createContext<"light" | "dark">("light");

function App() {
  const [theme, setTheme] = useState<"light" | "dark">("light");
  return (
    <ThemeContext.Provider value={theme}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Badge() {
  const theme = useContext(ThemeContext);
  return <span className={theme}>Current theme: {theme}</span>;
}

Erro comum: Passar um novo objeto literal como valor do provider a cada renderização, como value={{ user, setUser }}. Cada renderização cria um objeto novo, então todos os consumidores renderizam de novo mesmo quando nada mudou. Envolva o valor em useMemo, ou separe os valores que mudam pouco dos que mudam muito em dois contexts. Confira também se o provider está acima do componente: chamar useContext no mesmo componente que renderiza o provider retorna o valor padrão.

Hooks de ref

useRef

O que faz: Dá a você uma caixa (ref.current) que sobrevive às renderizações. Mudá-la não causa renderização. Use para nós do DOM, IDs de timers e outros valores que a tela não mostra.

const inputRef = useRef<HTMLInputElement>(null);
const clicks = useRef(0);

<input ref={inputRef} />
<button onClick={() => inputRef.current?.focus()}>Focus</button>
<button onClick={() => { clicks.current += 1; }}>Count silently</button>

Erro comum: Mostrar ref.current no JSX e esperar que ele se atualize. Refs não disparam renderizações, então a tela só acompanha quando outra coisa renderiza de novo (teste o botão "Somar na ref" e depois "Renderizar de novo" acima). Se o valor precisa aparecer na tela, ele pertence ao estado. Evite também ler ou gravar refs durante a renderização, exceto para uma inicialização preguiçosa feita uma única vez.

useImperativeHandle

O que faz: Personaliza o que um componente pai recebe quando coloca uma ref no seu componente, para que você exponha alguns métodos com nome em vez do nó do DOM inteiro.

type Handle = { shake: () => void; clear: () => void };

// React 18: wrap in forwardRef. In React 19 you can accept ref as a normal prop.
const FancyInput = forwardRef<Handle, Props>(function FancyInput(props, ref) {
  const el = useRef<HTMLInputElement>(null);
  useImperativeHandle(ref, () => ({
    shake() { /* animate */ el.current?.focus(); },
    clear() { if (el.current) el.current.value = ""; },
  }), []);
  return <input ref={el} />;
});

// parent
const ref = useRef<Handle>(null);
<FancyInput ref={ref} />
<button onClick={() => ref.current?.shake()}>Shake</button>

Erro comum: Usá-lo para controlar coisas que as props conseguem expressar. Se um componente pai chama ref.current.open() para mostrar um modal, uma prop isOpen costuma ser mais simples e fácil de acompanhar. Reserve os handles imperativos para foco, rolagem, play/pause e animações. Esquecer o array de dependências (o terceiro argumento) também recria o handle a cada renderização.

Hooks de efeito

useEffect

O que faz: Executa código depois que o React atualizou a tela, para manter um componente sincronizado com algo fora do React: um timer, uma assinatura, uma requisição de rede, um widget que não é React. Retorne uma função de cleanup para desfazer.

useEffect(() => {
  if (!running) return;
  const id = setInterval(() => setSeconds((s) => s + 1), 1000);
  return () => clearInterval(id); // runs before the next effect and on unmount
}, [running]);

Erro comum: Usar um effect para calcular algo que você poderia calcular durante a renderização, como useEffect(() => setFullName(first + " " + last), [first, last]). Isso custa uma renderização extra e pode mostrar valores desatualizados por um frame. Escreva const fullName = first + " " + last no lugar. O segundo bug mais comum é a falta de cleanup: no Strict Mode de desenvolvimento, o React monta, desmonta e monta de novo cada componente uma vez, então um effect sem cleanup vai assinar algo ou iniciar um timer duas vezes. Essa execução dupla é uma pista, não um bug do React.

useLayoutEffect

O que faz: Tem o mesmo formato do useEffect, mas roda depois que o React muda o DOM e antes de o navegador pintar. Use para medir um elemento e ajustar o layout, para que os usuários nunca vejam a posição errada.

const boxRef = useRef<HTMLSpanElement>(null);
const [width, setWidth] = useState(0);

useLayoutEffect(() => {
  setWidth(boxRef.current!.getBoundingClientRect().width);
}, [text]);

Erro comum: Usá-lo por padrão. Ele bloqueia a pintura, então um trabalho lento dentro dele faz a página parecer travada. Use useEffect, a menos que você veja uma piscada que só uma medição antes da pintura resolve (tooltips, posicionamento de popovers, dimensionamento automático). Ele também não faz nada durante a renderização no servidor, o que gera um aviso em algumas configurações.

useInsertionEffect

O que faz: Roda antes de qualquer layout effect para que uma biblioteca possa inserir regras <style> antes que algo leia o layout. Ele existe para autores de bibliotecas CSS-in-JS.

function useScopedRule(selector: string, rule: string) {
  useInsertionEffect(() => {
    const style = document.createElement("style");
    style.textContent = `${selector} { ${rule} }`;
    document.head.appendChild(style);
    return () => style.remove();
  }, [selector, rule]);
}

Erro comum: Usá-lo em código de aplicação, ou tentar atualizar o estado a partir dele. Não dá para agendar atualizações dentro dele e as refs ainda não estão ligadas. Se você não está escrevendo uma biblioteca de estilos, quase certamente quer useEffect ou useLayoutEffect.

useEffectEvent

O que faz: React 19.2 ou mais recente. Envolve uma função para que um effect possa chamá-la e sempre enxergar as props e o estado mais recentes, sem listar esses valores como dependências. Use para a parte de "evento" de um effect, como registrar logs ou mostrar uma notificação com o tema atual.

// React 19.2+ only. Not run on this page.
import { useEffect, useEffectEvent } from "react";

function ChatRoom({ roomId, theme }) {
  const onConnected = useEffectEvent(() => {
    showNotification("Connected", theme); // always the latest theme
  });

  useEffect(() => {
    const conn = createConnection(roomId);
    conn.on("connected", () => onConnected());
    conn.connect();
    return () => conn.disconnect();
  }, [roomId]); // theme is not a dependency, and onConnected must not be listed
}

Erro comum: Colocar o effect event no array de dependências, ou chamá-lo durante a renderização, ou passá-lo para outros componentes. A documentação do React diz para chamar effect events só de dentro de effects (ou de outros effect events), e a regra de lint avisa quando você lista um como dependência. No React 18, o substituto habitual é o padrão "latest ref" usado pelo useInterval mais abaixo nesta página.

Hooks de desempenho

useMemo

O que faz: Guarda em cache o resultado de um cálculo e só recalcula quando as dependências mudam.

const filtered = useMemo(
  () => items.filter((item) => item.includes(query)),
  [items, query],
);

Erro comum: Envolver tudo com ele. Memoizar trabalho barato como a + b ou um map curto adiciona código e custo de comparação sem ganho nenhum. Meça primeiro (React DevTools Profiler) e memoize cálculos lentos, valores passados a filhos com memo e valores usados como dependências de effects. Uma armadilha relacionada: uma dependência que é um objeto novo a cada renderização ({ sort: "asc" }) faz o memo recalcular todas as vezes.

useCallback

O que faz: Retorna a mesma instância da função entre renderizações até uma dependência mudar. Isso importa quando a função é passada a um filho envolvido em memo, ou usada como dependência de um effect.

const handleAdd = useCallback(() => setCount((c) => c + 1), []);

const Child = memo(function Child({ onClick }: { onClick: () => void }) {
  return <button onClick={onClick}>Add</button>;
});

<Child onClick={handleAdd} />

Erro comum: Usar useCallback quando o filho não é memoizado. Sem memo no filho, ele renderiza junto com o pai de qualquer jeito, então a função estável não muda nada. A demonstração ao vivo mostra os dois filhos: só o que recebe a função em cache pula renderizações. Closures desatualizadas são a outra armadilha: se o callback lê count mas a lista de dependências está vazia, ele continua vendo o primeiro valor. Use uma função de atualização ou liste a dependência.

useTransition

O que faz: Marca uma atualização de estado como não urgente. O React mantém a tela atual responsiva enquanto prepara a nova e dá a você um indicador isPending para sinalizar o carregamento.

const [isPending, startTransition] = useTransition();

function selectTab(next: string) {
  startTransition(() => {
    setTab(next);
  });
}

Erro comum: Envolver o próprio estado de um campo de texto em uma transition. Inputs controlados precisam atualizar com urgência, senão a digitação parece quebrada. Mantenha o estado do input normal e mova a atualização lenta e derivada para a transition (ou use useDeferredValue). No React 18 a função passada a startTransition precisa ser síncrona; o React 19 também aceita funções assíncronas.

useDeferredValue

O que faz: Dá a você uma cópia de um valor que pode ficar para trás. As partes rápidas da interface usam o valor real; uma parte lenta renderiza com o valor adiado e alcança o atual quando o navegador está ocioso.

const [query, setQuery] = useState("");
const deferredQuery = useDeferredValue(query);
const isStale = query !== deferredQuery;

<input value={query} onChange={(e) => setQuery(e.target.value)} />
<div style={{ opacity: isStale ? 0.5 : 1 }}>
  <SlowResults query={deferredQuery} />
</div>

Erro comum: Esquecer de envolver o filho lento em memo. Se o filho renderiza junto com o pai a cada tecla de qualquer jeito, adiar a prop dele não ajuda. Ele também não é um debounce: não espera um tempo fixo e não reduz as requisições de rede. Para ter menos requisições, use debounce (veja useDebounce abaixo).

Outros hooks

useId

O que faz: Gera um ID que é único dentro do app e idêntico no servidor e no cliente, para htmlFor, aria-describedby e atributos parecidos.

function Field({ label }: { label: string }) {
  const id = useId();
  return (
    <>
      <label htmlFor={id}>{label}</label>
      <input id={id} />
    </>
  );
}

Erro comum: Usá-lo para criar keys de listas. As keys devem vir dos seus dados (um ID do banco de dados), não de um hook. Evite também Math.random() para IDs de elementos em apps renderizados no servidor: o servidor e o navegador geram números diferentes e a hidratação falha, que é justamente o problema que o useId resolve.

useSyncExternalStore

O que faz: Inscreve um componente em uma fonte de dados que não pertence ao React (uma API do navegador, uma store global) e lê um snapshot que se mantém consistente durante a renderização concorrente.

function subscribe(callback: () => void) {
  window.addEventListener("online", callback);
  window.addEventListener("offline", callback);
  return () => {
    window.removeEventListener("online", callback);
    window.removeEventListener("offline", callback);
  };
}

const online = useSyncExternalStore(
  subscribe,
  () => navigator.onLine, // client snapshot
  () => true,             // server snapshot
);

Erro comum: Definir o subscribe inline dentro do componente. Uma função nova a cada renderização faz o React cancelar e refazer a assinatura todas as vezes. Defina fora do componente ou envolva em useCallback. A função de snapshot também precisa retornar o mesmo valor quando nada mudou: retornar um objeto ou array novo a cada chamada causa um loop infinito de renderização.

useDebugValue

O que faz: Adiciona um rótulo a um custom hook no React DevTools. Não tem efeito nenhum no que os usuários veem.

function useOnlineStatus() {
  const online = useSyncExternalStore(subscribe, () => navigator.onLine);
  useDebugValue(online ? "Online" : "Offline");
  return online;
}

Erro comum: Adicioná-lo a todo custom hook ou fazer formatação cara dentro dele. Use em hooks compartilhados cujo estado é difícil de ler no DevTools, e passe um formatador como segundo argumento se o rótulo for caro de montar: useDebugValue(date, d => d.toISOString()).

Hooks do React 19 (só código, não rodam no React 18)

Somente React 19. Os quatro hooks desta seção (use, useActionState, useOptimistic, useFormStatus) não são exportados pelo React 18. Esta página roda em React 18, então eles aparecem como código que você pode copiar para um projeto React 19, sem demonstrações ao vivo. O useEffectEvent, visto acima, exige React 19.2.

useActionState

O que faz: Envolve uma action de formulário e dá a você o resultado mais recente mais um indicador de pendência. A action recebe o estado anterior e o FormData enviado.

// React 19+
import { useActionState } from "react";

async function subscribe(prev: { error?: string; ok?: boolean }, formData: FormData) {
  const email = String(formData.get("email") ?? "");
  if (!email.includes("@")) return { error: "Enter a valid email" };
  await saveEmail(email);
  return { ok: true };
}

function Newsletter() {
  const [state, formAction, isPending] = useActionState(subscribe, {});
  return (
    <form action={formAction}>
      <input name="email" type="email" />
      <button disabled={isPending}>{isPending ? "Saving..." : "Subscribe"}</button>
      {state.error && <p role="alert">{state.error}</p>}
      {state.ok && <p>Thanks, you are subscribed.</p>}
    </form>
  );
}

Erro comum: Esquecer que o primeiro argumento da action é o estado anterior. Escrever async function subscribe(formData) significa que formData na verdade é o estado antigo e o .get lança um erro. Note também que ele se chamou brevemente useFormState (em react-dom) durante o período canary do React 19; a documentação atual usa useActionState de react.

useOptimistic

O que faz: Mostra uma versão temporária e otimista do estado enquanto uma action assíncrona roda, e volta ao estado real quando ela termina.

// React 19+
import { useOptimistic } from "react";

function Thread({ messages, send }: Props) {
  const [optimistic, addOptimistic] = useOptimistic(
    messages,
    (current, text: string) => [...current, { text, sending: true }],
  );

  async function action(formData: FormData) {
    const text = String(formData.get("text"));
    addOptimistic(text);
    await send(text); // parent updates `messages` when this resolves
  }

  return (
    <form action={action}>
      {optimistic.map((m, i) => <p key={i}>{m.text}{m.sending && " (sending)"}</p>)}
      <input name="text" />
    </form>
  );
}

Erro comum: Chamar o setter otimista fora de uma action ou transition. O React espera que ele esteja dentro de uma action de formulário ou de startTransition; em outros lugares o valor otimista aparece e some na hora (o React avisa em desenvolvimento). E sempre trate a falha: se send lançar um erro, mostre uma mensagem de erro para que a mensagem não suma sem aviso.

use

O que faz: Lê o valor de uma promise (suspendendo até ela resolver) ou de um context. Diferente dos outros hooks, ele pode ser chamado dentro de condições e loops.

// React 19+
import { use, Suspense } from "react";

function Comments({ commentsPromise }: { commentsPromise: Promise<Comment[]> }) {
  const comments = use(commentsPromise); // suspends until resolved
  return comments.map((c) => <p key={c.id}>{c.text}</p>);
}

function Page({ commentsPromise }) {
  return (
    <Suspense fallback={<p>Loading comments...</p>}>
      <Comments commentsPromise={commentsPromise} />
    </Suspense>
  );
}

// Conditional context read, which useContext cannot do:
function Heading({ show }: { show: boolean }) {
  if (!show) return null;
  const theme = use(ThemeContext);
  return <h1 className={theme}>Hello</h1>;
}

Erro comum: Criar a promise dentro do componente que chama use, como use(fetch(url)). Cada renderização cria uma promise nova, então o componente fica suspenso para sempre. Crie a promise uma vez (em um server component, um loader, um cache ou um componente pai) e passe para baixo. O use também não pode ficar dentro de try/catch; trate os erros com um error boundary.

useFormStatus

O que faz: Importado de react-dom. Lê o status do <form> pai: pending, data, method e action.

// React 19+
import { useFormStatus } from "react-dom";

function SubmitButton() {
  const { pending } = useFormStatus();
  return <button type="submit" disabled={pending}>{pending ? "Sending..." : "Send"}</button>;
}

function ContactForm() {
  return (
    <form action={sendMessage}>
      <textarea name="message" />
      <SubmitButton />
    </form>
  );
}

Erro comum: Chamá-lo no mesmo componente que renderiza o <form>. Ele só informa sobre um formulário acima do componente que o chama, então no próprio componente do formulário pending é sempre false. Mova o botão para um componente próprio, como acima.

O array de dependências, explicado

useEffect, useLayoutEffect, useMemo, useCallback e useImperativeHandle recebem uma lista de valores como último argumento. O React compara cada item com o valor da renderização anterior usando Object.is. Se algum item mudou, o effect roda de novo (ou o memo recalcula). Essa única regra explica todos os casos:

Você escreveO effect rodaUso típico
useEffect(fn)depois de toda renderizaçãoraro; geralmente sinal de que algo deveria ser calculado durante a renderização
useEffect(fn, [])uma vez depois da montagem (duas vezes no Strict Mode de desenvolvimento), cleanup na desmontagemassinaturas e configurações que não usam props nem estado
useEffect(fn, [a, b])depois da montagem e sempre que a ou b mudarbuscar dados por ID, sincronizar com uma prop

Clique nos botões: mudar b executa de novo só o effect sem array, mudar a executa de novo esse e também o effect [a], e o effect [] fica quieto depois da montagem.

Cinco regras que evitam a maioria dos bugs de dependências

  1. Liste todo valor reativo que o callback lê. Props, estado e qualquer coisa declarada no corpo do componente. Deixar um de fora gera uma closure desatualizada: o callback fica com o valor de uma renderização antiga. Instale o eslint-plugin-react-hooks e deixe o exhaustive-deps dizer o que está faltando.
  2. Objetos e funções criados durante a renderização são novos a cada vez. { id } ou () => {} nunca é igual ao anterior pelo Object.is, então eles executam o effect de novo a cada renderização. Mova-os para dentro do effect, para fora do componente, ou memoize-os.
  3. Dependa de valores primitivos sempre que puder. [user.id] é mais estável que [user] quando só o ID importa.
  4. Use funções de atualização para eliminar dependências de estado. setCount(c => c + 1) não lê count, então count não precisa ser listado.
  5. Não silencie o linter para "consertar" um loop. Um loop infinito geralmente significa que o effect define um estado que também é dependência, ou depende de um objeto novo. Corrija a causa. Para valores que você quer ler sem reagir a eles, use useEffectEvent no React 19.2+, ou uma ref mantida atualizada no React 18.
// Stale closure: logs 0 forever
useEffect(() => {
  const id = setInterval(() => console.log(count), 1000);
  return () => clearInterval(id);
}, []); // count is missing

// Fixed: re-subscribe when count changes
useEffect(() => {
  const id = setInterval(() => console.log(count), 1000);
  return () => clearInterval(id);
}, [count]);

// Fetch with cleanup so a slow old response cannot overwrite a newer one
useEffect(() => {
  const controller = new AbortController();
  fetch(`/api/users/${userId}`, { signal: controller.signal })
    .then((r) => r.json())
    .then(setUser)
    .catch((err) => { if (err.name !== "AbortError") setError(err); });
  return () => controller.abort();
}, [userId]);

12 custom hooks para copiar no seu projeto

Um custom hook é uma função cujo nome começa com use e que chama outros hooks. Ele permite que vários componentes compartilhem lógica com estado; cada componente que o chama recebe o seu próprio estado independente. Os hooks abaixo foram escritos para esta página em TypeScript, funcionam no React 18 e 19 e não têm dependências. Cada um termina com uma nota sobre quando não usá-lo.

1. useDebounce

Retorna um valor que só se atualiza depois que a entrada para de mudar por delay milissegundos. Bom para caixas de busca que chamam uma API.

import { useEffect, useState } from "react";

export function useDebounce<T>(value: T, delay = 300): T {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => {
    const id = window.setTimeout(() => setDebounced(value), delay);
    return () => window.clearTimeout(id);
  }, [value, delay]);
  return debounced;
}

// const debouncedQuery = useDebounce(query, 400);
// useEffect(() => { search(debouncedQuery); }, [debouncedQuery]);

Quando não usar: Quando você só precisa que a interface continue responsiva enquanto renderiza uma lista grande. Esse é um problema de renderização, e o useDeferredValue resolve sem um atraso artificial. Dispense também na validação de formulários que deve parecer instantânea.

2. useLocalStorage

Funciona como o useState, mas salva o valor no localStorage e o restaura na próxima visita.

import { useEffect, useState } from "react";

export function useLocalStorage<T>(key: string, initial: T) {
  const [value, setValue] = useState<T>(() => {
    try {
      const raw = window.localStorage.getItem(key);
      return raw === null ? initial : (JSON.parse(raw) as T);
    } catch {
      return initial; // private mode, blocked storage or bad JSON
    }
  });

  useEffect(() => {
    try {
      window.localStorage.setItem(key, JSON.stringify(value));
    } catch {
      /* storage full or blocked: keep the in-memory value */
    }
  }, [key, value]);

  return [value, setValue] as const;
}

Quando não usar: Para qualquer coisa sensível (tokens, dados pessoais), já que qualquer script da página consegue ler o localStorage. Também não serve para páginas renderizadas no servidor sem cuidado: o servidor não tem window, então leia o armazenamento depois da montagem para evitar uma divergência de hidratação. Ele não sincroniza entre abas; adicione um listener do evento storage se precisar disso.

3. useMediaQuery

Retorna true enquanto uma media query CSS corresponde, e se atualiza quando isso muda.

import { useCallback, useSyncExternalStore } from "react";

export function useMediaQuery(query: string): boolean {
  const subscribe = useCallback((onChange: () => void) => {
    const mql = window.matchMedia(query);
    mql.addEventListener("change", onChange);
    return () => mql.removeEventListener("change", onChange);
  }, [query]);

  return useSyncExternalStore(
    subscribe,
    () => window.matchMedia(query).matches,
    () => false, // server snapshot
  );
}

// const isDesktop = useMediaQuery("(min-width: 1024px)");
// const reduceMotion = useMediaQuery("(prefers-reduced-motion: reduce)");

Quando não usar: Para mudanças de layout que o CSS consegue fazer sozinho. Esconder ou reorganizar elementos com breakpoints do Tailwind (md:hidden) funciona antes de o JavaScript carregar e nunca pisca. Use o hook quando precisar renderizar componentes diferentes ou deixar de carregar algo por completo.

4. useToggle

Um booleano com uma função toggle estável, para menus, modais e seções expansíveis.

import { useCallback, useState } from "react";

export function useToggle(initial = false) {
  const [on, setOn] = useState(initial);
  const toggle = useCallback(() => setOn((v) => !v), []);
  return [on, toggle, setOn] as const;
}

// const [open, toggleOpen, setOpen] = useToggle();

Quando não usar: Quando o estado tem mais de dois valores (fechado, abrindo, aberto, erro). Use uma união de strings com useState ou useReducer em vez de empilhar vários toggles.

5. usePrevious

Retorna o valor da renderização anterior, útil para mostrar uma mudança ("subiu de 12") ou reagir a uma transição.

import { useEffect, useRef } from "react";

export function usePrevious<T>(value: T): T | undefined {
  const ref = useRef<T>();
  useEffect(() => {
    ref.current = value;
  }, [value]);
  return ref.current;
}

Quando não usar: Para decidir se deve atualizar o estado. A documentação do React mostra um padrão mais limpo para isso: guarde o valor anterior no estado e compare durante a renderização. Este hook lê uma ref durante a renderização, o que funciona hoje, mas é o tipo de código que as orientações do React pedem para manter raro.

6. useOnClickOutside

Chama um handler quando o usuário pressiona em qualquer lugar fora de um elemento. Típico para fechar um menu ou popover personalizado.

import { useEffect, useRef, type RefObject } from "react";

export function useOnClickOutside(
  ref: RefObject<HTMLElement>,
  handler: (event: PointerEvent) => void,
) {
  const saved = useRef(handler);
  useEffect(() => { saved.current = handler; }, [handler]);

  useEffect(() => {
    const listener = (event: PointerEvent) => {
      if (!ref.current || ref.current.contains(event.target as Node)) return;
      saved.current(event);
    };
    document.addEventListener("pointerdown", listener);
    return () => document.removeEventListener("pointerdown", listener);
  }, [ref]);
}

Quando não usar: Se você já usa uma biblioteca headless (Radix, Headless UI) para o menu ou diálogo: elas cuidam dos cliques externos, da tecla Escape e do retorno do foco para você. Um menu feito à mão só com este hook geralmente fica sem suporte a teclado.

7. useInterval

Um setInterval que sempre chama o callback mais recente e pode ser pausado passando null como delay.

import { useEffect, useRef } from "react";

export function useInterval(callback: () => void, delay: number | null) {
  const saved = useRef(callback);
  useEffect(() => { saved.current = callback; }, [callback]);

  useEffect(() => {
    if (delay === null) return;
    const id = window.setInterval(() => saved.current(), delay);
    return () => window.clearInterval(id);
  }, [delay]);
}

// useInterval(() => setTicks((t) => t + 1), running ? 1000 : null);

Quando não usar: Para animações (use CSS ou requestAnimationFrame) ou para relógios precisos: os intervalos se desviam e os navegadores os desaceleram em abas em segundo plano. Para uma contagem regressiva, calcule o tempo restante a partir de um timestamp final fixo a cada tick.

8. useWindowSize

Retorna a largura e a altura atuais da janela e se atualiza no redimensionamento.

import { useSyncExternalStore } from "react";

function subscribe(onChange: () => void) {
  window.addEventListener("resize", onChange);
  return () => window.removeEventListener("resize", onChange);
}

export function useWindowSize() {
  const width = useSyncExternalStore(subscribe, () => window.innerWidth, () => 0);
  const height = useSyncExternalStore(subscribe, () => window.innerHeight, () => 0);
  return { width, height };
}

Quando não usar: Para breakpoints: o useMediaQuery só renderiza de novo quando a query muda de estado, enquanto este renderiza a cada pixel de redimensionamento. Para o tamanho de um elemento em vez da janela, use um ResizeObserver. (Os dois snapshots separados são de propósito: retornar um objeto novo de uma única função de snapshot entraria em loop para sempre.)

9. useCopyToClipboard

Copia texto com a Clipboard API e expõe um indicador copied de curta duração para dar retorno no botão.

import { useCallback, useState } from "react";

export function useCopyToClipboard(resetAfter = 1500) {
  const [copied, setCopied] = useState(false);
  const copy = useCallback(async (text: string) => {
    try {
      await navigator.clipboard.writeText(text);
      setCopied(true);
      window.setTimeout(() => setCopied(false), resetAfter);
      return true;
    } catch {
      setCopied(false); // insecure context or permission denied
      return false;
    }
  }, [resetAfter]);
  return [copied, copy] as const;
}

Quando não usar: Em páginas servidas por HTTP simples: o navigator.clipboard precisa de um contexto seguro (HTTPS ou localhost). Sempre mostre uma alternativa, como selecionar o texto, quando ele retornar false.

10. useFetch

Carrega JSON de uma URL, acompanha os estados de carregamento e erro e cancela a requisição quando a URL muda ou o componente é desmontado.

import { useEffect, useState } from "react";

type FetchState<T> = { data: T | null; error: Error | null; loading: boolean };

export function useFetch<T>(url: string | null): FetchState<T> {
  const [state, setState] = useState<FetchState<T>>({ data: null, error: null, loading: !!url });

  useEffect(() => {
    if (!url) return;
    const controller = new AbortController();
    setState((s) => ({ ...s, loading: true, error: null }));

    fetch(url, { signal: controller.signal })
      .then((res) => {
        if (!res.ok) throw new Error(`Request failed with status ${res.status}`);
        return res.json() as Promise<T>;
      })
      .then((data) => setState({ data, error: null, loading: false }))
      .catch((error: Error) => {
        if (error.name === "AbortError") return; // superseded or unmounted
        setState({ data: null, error, loading: false });
      });

    return () => controller.abort();
  }, [url]);

  return state;
}

Quando não usar: Em apps maiores. Ele não tem cache, deduplicação, novas tentativas nem revalidação, então dois componentes pedindo a mesma URL fazem duas requisições. Use o carregamento de dados do seu framework ou uma biblioteca de dados (TanStack Query, SWR) assim que precisar de qualquer uma dessas coisas. A própria documentação do React faz a mesma recomendação para buscar dados em effects.

11. useEventListener

Liga um event listener à janela (ou a qualquer elemento) e sempre chama o handler mais recente, sem refazer a assinatura a cada renderização.

import { useEffect, useRef } from "react";

export function useEventListener<K extends keyof WindowEventMap>(
  type: K,
  handler: (event: WindowEventMap[K]) => void,
  target: Window | HTMLElement | null = typeof window === "undefined" ? null : window,
) {
  const saved = useRef(handler);
  useEffect(() => { saved.current = handler; }, [handler]);

  useEffect(() => {
    if (!target) return;
    const listener = (event: Event) => saved.current(event as WindowEventMap[K]);
    target.addEventListener(type, listener);
    return () => target.removeEventListener(type, listener);
  }, [type, target]);
}

// useEventListener("keydown", (e) => { if (e.key === "Escape") close(); });

Quando não usar: Para eventos que o React já trata nos elementos que você renderiza: use onClick, onKeyDown e companhia direto no JSX. Recorra a este hook só para window, document ou elementos de terceiros.

12. useIsMounted

Retorna uma função que diz se o componente ainda está na tela, para código assíncrono legado que define estado depois de um await.

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

export function useIsMounted() {
  const mounted = useRef(false);
  useEffect(() => {
    mounted.current = true;
    return () => { mounted.current = false; };
  }, []);
  return useCallback(() => mounted.current, []);
}

// const isMounted = useIsMounted();
// const data = await load(); if (isMounted()) setData(data);

Quando não usar: Como padrão. O React 18 removeu o aviso sobre definir estado em componentes desmontados, e a melhor solução é cancelar o próprio trabalho (um AbortController, como no useFetch) ou ignorar resultados desatualizados com um indicador dentro do cleanup do effect. Reserve este hook para código antigo que você ainda não consegue reestruturar.

De qual hook eu preciso? Uma lista rápida para decidir

Está montando uma biblioteca de componentes em vez de hooks avulsos? A nossa comparação de bibliotecas de UI para React mostra os mesmos seis componentes em shadcn/ui, Radix, Headless UI, daisyUI, Flowbite e MUI, e a cheat sheet de Tailwind cobre as classes utilitárias usadas nas demonstrações desta página. Peças prontas de React e Tailwind estão na galeria de componentes.

Perguntas frequentes

Quantos hooks nativos o React tem?

No momento em que escrevemos (outubro de 2026, React 19.3), a referência de hooks do React lista 17 hooks no próprio React: useState, useReducer, useContext, useRef, useImperativeHandle, useEffect, useLayoutEffect, useInsertionEffect, useEffectEvent, useMemo, useCallback, useTransition, useDeferredValue, useDebugValue, useId, useSyncExternalStore e useActionState. O React 19 também adiciona o useOptimistic e a API use, e o React DOM adiciona o useFormStatus.

Qual a diferença entre useMemo e useCallback?

O useMemo guarda em cache o resultado de chamar uma função, enquanto o useCallback guarda a própria função. useCallback(fn, deps) equivale a useMemo(() => fn, deps). Use o useMemo para valores caros e o useCallback para funções passadas a filhos memoizados ou usadas como dependências de effects.

Por que o meu useEffect roda duas vezes?

Em desenvolvimento com o Strict Mode ligado, o React monta cada componente, desmonta e monta de novo para ajudar você a encontrar effects sem cleanup. Isso não acontece no build de produção. Se rodar duas vezes causa um bug, adicione uma função de cleanup que desfaça o que o effect iniciou.

O que significa um array de dependências vazio no useEffect?

Um array vazio diz ao React que o effect não lê props nem estado, então ele roda uma vez depois que o componente é montado e o cleanup roda quando o componente é desmontado. Se o effect lê props ou estado, liste-os, senão ele continua usando os valores da primeira renderização.

Posso usar hooks do React 19 como use ou useActionState no React 18?

Não. use, useActionState, useOptimistic e useFormStatus não são exportados pelo React 18, e o useEffectEvent exige React 19.2 ou mais recente. No React 18 dá para ter um comportamento parecido com useState mais event handlers nos formulários, e com uma ref que guarda o callback mais recente no lugar do useEffectEvent.

O que faz de uma função um custom hook?

O nome dela começa com use e ela chama pelo menos um outro hook. Essa convenção de nome permite que as regras de lint do React verifiquem se ela segue as regras dos hooks. Cada componente que chama um custom hook recebe o seu próprio estado separado; os hooks compartilham lógica, não dados.

Ao vivo: {title}

+3 (updater)

Zerar

Ler a seção do useReducer

Adicionar o seu próprio item

Novo item

Nova tarefa

Limpar concluídas

Eu li "{theme}" do context

Alternar o valor do provider

Clique no botão para me focar

Alvo do foco

Focar o campo

Somar na ref (sem renderizar)

Renderizar de novo

Valor da ref na última renderização: {n}

Digite aqui

Campo do filho

O pai chama shake()

O pai chama clear()

effect rodou: intervalo iniciado

cleanup rodou: intervalo limpo

Pausar

Retomar

Mude o meu tamanho digitando

Texto a medir

Medido antes da pintura: {n}px de largura

Cor da borda

Esta regra foi injetada no head da página

Filtrar 4,000 itens

Filtrar itens

Renderização não relacionada ({n})

{n} resultados. O filtro rodou {runs} vezes (renderizações não relacionadas não o executam).

{label} (filho renderizou {n}x)

função inline

Renderizar o pai de novo

Contagem: {n}

Sobre

Lista lenta

Contato

Trocando (a aba antiga continua clicável)...

Mostrando: {tab}

Linha

Conteúdo da aba {tab}.

Resultados renderizados para: "{query}"

Digite rápido: o campo continua responsivo

Buscar

Nome

E-mail

Rede:

Largura da janela:

Redimensione a janela ou desligue a sua rede para ver a atualização.

Este componente chama um custom hook que se rotula como "{label}" no React DevTools. Abra a aba Components para ver o rótulo ao lado do hook.

Online

Offline

arrays de dependências

[sem array] rodou (renderização {n})

[] rodou uma vez na montagem

[a] rodou, a = {n}

Mudar a ({n})

Mudar b ({n})

Limpar log

Digite e pare por meio segundo

Campo com debounce

Imediato: "{text}" · Com debounce: "{debounced}"

Digite e recarregue a página

Nome salvo

Salvo só neste navegador.

Salvo só neste navegador: "{name}".

useMediaQuery: o viewport tem {size}, o tema do sistema é {theme}.

768px ou mais

menos de 768px

Ligado

Desligado

Contar {n}

o anterior era {value}

nada ainda

Parar

Iniciar

{n} ticks

Copiado

Copiar um comando

Abrir menu

Clique em qualquer lugar fora do menu para fechá-lo.

Comece grátis · Sem cartão de crédito

Relacionados: Guias

Produto

Para quem é

Recursos

Recursos

Empresa

Mapa do site