Cascata de re-renders do React: quando um clique re-renderiza a árvore inteira
O que acontece
No React, uma atualização de estado re-renderiza o componente e, por padrão, todos os seus filhos. Se o estado fica no alto da árvore (um context, um store de nível de página), uma única tecla ou clique pode re-renderizar centenas de componentes — a maioria mostrando exatamente o mesmo de antes.
Cada render é barato; milhares deles em um único manipulador não são. O trabalho cai na fase de processing time do INP.
Como reconhecer
- O Profiler do React DevTools mostra flame graphs largos: componentes re-renderizam sem que suas props mudem visivelmente.
- Digitar em um input controlado trava; cada tecla re-renderiza listas ou tabelas grandes.
- O processing time domina o INP, e a pilha no DevTools Performance está cheia de internals do React.
Como corrigir
- Desça o estado: mantenha-o no menor componente que precisa dele em vez de um componente de nível de página ou context.
- Envolva subárvores caras em React.memo e estabilize as props que você passa a elas com useCallback/useMemo — memo é inútil se um novo callback é criado a cada render.
- Marque atualizações não urgentes (filtrar uma lista enquanto digita) com useTransition para que o render urgente (o próprio input) pinte primeiro.
Problema
const Page = () => {
const [query, setQuery] = useState("")
// every keystroke re-renders Page → Header, Sidebar, BigTable, Footer
return <>
<SearchInput value={query} onChange={setQuery} />
<BigTable rows={filterRows(rows, query)} />
</>
}
Correção
const BigTable = React.memo(({ rows }) => /* ... */)
const Page = () => {
const [query, setQuery] = useState("")
const [isPending, startTransition] = useTransition()
const rows = useMemo(() => filterRows(allRows, query), [query])
const onChange = (q) =>
startTransition(() => setQuery(q)) // input paints first, table follows
return <>
<SearchInput onChange={onChange} />
<BigTable rows={rows} />
</>
}
Escaneie seu site gratuitamente →