Hook useTransition
Marque atualizações de estado como não urgentes para que não bloqueiem a entrada do usuário.
Busque em todas as páginas da documentação
Marque atualizações de estado como não urgentes para que não bloqueiem a entrada do usuário.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Cartão de receita de referência rápida - pronto para copiar e colar.
const [isPending, startTransition] = useTransition();
// Envolva uma atualização de estado lenta
startTransition(() => {
setSearchResults(filterLargeList(query));
});
// Mostre o estado pendente
{isPending && <Spinner />}Quando usar isso: Uma atualização de estado causa re-renderização cara (filtrar uma lista grande, alternar abas com conteúdo pesado) e você quer que a UI permaneça responsiva durante a atualização.
"use client";
import { useState, useTransition } from "react";
const ALL_ITEMS = Array.from({ length: 10000 }, (_, i) => ({
id: i,
name: `Item ${i}`,
category: ["Electronics", "Books", "Clothing", "Food"][i % 4],
}));
export function FilterableList() {
const [query, setQuery] = useState("");
const [filtered, setFiltered] = useState(ALL_ITEMS);
const [isPending, startTransition] = useTransition();
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
const value = e.target.value;
setQuery(value); // Urgente: atualize o input imediatamente
startTransition(() => {
// Não urgente: filtre a lista grande
setFiltered(
ALL_ITEMS.filter((item) =>
item.name.toLowerCase().includes(value.toLowerCase())
)
);
});
}
return (
<div className="space-y-3">
<input
value={query}
onChange={handleChange}
placeholder="Pesquisar 10.000 itens..."
className="border rounded px-3 py-2 w-full"
/>
{isPending && <p className="text-sm text-gray-500">Atualizando...</p>}
<ul className="max-h-64 overflow-y-auto text-sm">
{filtered.slice(0, 100).map((item) => (
<li key={item.id} className="py-0.5">
{item.name} - {item.category}
</li>
))}
</ul>
<p className="text-xs text-gray-400">{filtered.length} resultados</p>
</div>
);
}O que isso demonstra:
setQuery está fora de startTransitionisPending mostra um indicador de carregamento enquanto a transição está em andamentostartTransition informa ao React que a atualização de estado interna é não urgente.isPending é true, permitindo que você mostre um indicador de carregamento.startTransition também pode lidar com funções assíncronas, tornando-o útil para server actions.| Parâmetro | Tipo | Descrição |
|---|---|---|
| (nenhum) | - | useTransition não aceita parâmetros |
| Retorno | Tipo | Descrição |
|---|---|---|
isPending | boolean | true enquanto a transição está em andamento |
startTransition | (callback: () => void) => void | Envolve atualizações de estado para marcá-las como não urgentes |
Alternância de abas com Suspense:
const [tab, setTab] = useState("home");
const [isPending, startTransition] = useTransition();
function selectTab(nextTab: string) {
startTransition(() => {
setTab(nextTab);
});
}
return (
<div>
<nav className={isPending ? "opacity-50" : ""}>
<button onClick={() => selectTab("home")}>Home</button>
<button onClick={() => selectTab("posts")}>Posts</button>
</nav>
<Suspense fallback={<Spinner />}>
{tab === "home" ? <Home /> : <Posts />}
</Suspense>
</div>
);Transições assíncronas do React 19 com server actions:
const [isPending, startTransition] = useTransition();
function handleSubmit() {
startTransition(async () => {
const result = await saveToServer(formData);
setData(result); // Atualizações da UI após a resposta do servidor
});
}// isPending é sempre booleano, startTransition aceita () => void
const [isPending, startTransition] = useTransition();
// React 19: startTransition também aceita funções assíncronas
startTransition(async () => {
await serverAction();
});Envolver atualizações síncronas baratas - Usar startTransition para um simples setCount(count + 1) adiciona sobrecarga sem benefício. Correção: Use transições apenas para atualizações que causam re-renderizações caras.
Não separar o urgente do não urgente - Envolver tanto o estado do input quanto o estado do filtro em startTransition atrasa o input também. Correção: Mantenha atualizações urgentes (valor do input) fora de startTransition.
isPending permanece true por muito tempo - Se a transição causar a suspensão de um limite do Suspense, isPending permanecerá true até que o conteúdo suspenso seja resolvido. Correção: Este é o comportamento esperado; projete seus estados de carregamento de acordo.
startTransition deve ser síncrono (React 18) - No React 18, o callback deve chamar setState de forma síncrona, não dentro de um setTimeout ou após um await. Correção: No React 18, acione a atualização de estado de forma síncrona. No React 19, callbacks assíncronos são suportados.
Não é possível envolver estado não-React - Transições funcionam apenas com atualizações de estado do React (useState, useReducer). Atualizar um ref ou store externo dentro de startTransition não tem efeito. Correção: Certifique-se de que a atualização de estado seja um setter de estado do React.
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
useDeferredValue | Você quer adiar um valor específico sem controlar quando a atualização dispara | Você precisa de controle explícito sobre quais atualizações são não urgentes |
| Debouncing | Reduzir a frequência de operações caras (ex: chamadas de API) | Você quer que o React permaneça responsivo durante a própria renderização |
| Web Worker | A computação é intensiva em CPU e não deve bloquear a thread principal de forma alguma | O trabalho é renderizar componentes React |
Virtualização (react-window) | Renderizar milhares de elementos DOM | O gargalo é computação, não nós DOM |
Transições vs. debouncing: Debouncing atrasa a atualização inteiramente. Transições permitem que o React comece a renderizar imediatamente, mas interrompe se algo mais urgente chegar. Transições mantêm a UI antiga visível enquanto renderizam a nova.
useTransition envolve a própria atualização de estado, dando a você controle explícito sobre quais atualizações são não urgentes.useDeferredValue envolve o consumo do valor, adiando quando um filho re-renderiza com o novo valor.useTransition quando você controla a atualização de estado; use useDeferredValue quando você recebe o valor como uma prop.startTransition atrasa o input de refletir a digitação do usuário.isPending é definido automaticamente como true quando a transição começa e false quando ela é concluída.setLoading(true) / setLoading(false).isPending permanece true enquanto o conteúdo suspenso carrega.useState, setters do useReducer).startTransition não tem efeito.startTransition(async () => { await serverAction(); setState(result); }) é suportado.setState de forma síncrona - sem await dentro.isPending permanece true até que o conteúdo suspenso seja resolvido.setCount(count + 1), startTransition adiciona sobrecarga sem benefício.const [isPending, startTransition] = useTransition();
// isPending: boolean
// startTransition: (callback: () => void) => void
// React 19 também aceita assíncrono:
// startTransition: (callback: () => void | Promise<void>) => voidstartTransition no mesmo handler são agrupadas em uma única transição.startTransition como de baixa prioridade juntas.Revisado por Chris St. John·Última atualização: 19 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥