Cascata di re-render di React: quando un clic ri-renderizza l'intero albero
Cosa succede
In React, un aggiornamento di stato ri-renderizza il componente e, per impostazione predefinita, tutti i suoi figli. Se lo stato vive in alto nell'albero (un context, uno store a livello di pagina), una singola pressione di tasto o un clic può ri-renderizzare centinaia di componenti — la maggior parte dei quali mostra esattamente la stessa cosa di prima.
Ogni render è economico; migliaia in un singolo gestore non lo sono. Il lavoro finisce nella fase di processing time dell'INP.
Come riconoscerlo
- Il Profiler di React DevTools mostra flame graph larghi: i componenti si ri-renderizzano senza che le loro props cambino visibilmente.
- Scrivere in un input controllato è a scatti; ogni pressione di tasto ri-renderizza liste o tabelle grandi.
- Il processing time domina l'INP e lo stack in DevTools Performance è pieno di internals di React.
Come risolverlo
- Sposta lo stato in basso: tienilo nel componente più piccolo che ne ha bisogno invece che in un componente a livello di pagina o in un context.
- Avvolgi i sottoalberi costosi in React.memo e stabilizza le props che passi loro con useCallback/useMemo — memo è inutile se a ogni render viene creato un nuovo callback.
- Contrassegna gli aggiornamenti non urgenti (filtrare una lista mentre si digita) con useTransition così il render urgente (l'input stesso) viene disegnato per primo.
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)} />
</>
}
Soluzione
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} />
</>
}
Scansiona il tuo sito gratis →