React-Re-Render-Kaskade: wenn ein Klick den ganzen Baum neu rendert
Was passiert
In React rendert ein State-Update die Komponente und standardmäßig alle ihre Kinder neu. Lebt der State hoch im Baum (ein Context, ein Store auf Seitenebene), kann ein einzelner Tastendruck oder Klick Hunderte Komponenten neu rendern — die meisten zeigen genau dasselbe wie zuvor.
Ein Render ist billig; Tausende in einem Event-Handler nicht. Die Arbeit landet in der Processing-Time-Phase des INP.
So erkennst du es
- Der React DevTools Profiler zeigt breite Flame-Graphs: Komponenten rendern neu, ohne dass sich ihre Props sichtbar ändern.
- Das Tippen in ein kontrolliertes Input ruckelt; jeder Tastendruck rendert große Listen oder Tabellen neu.
- Die Processing Time dominiert den INP, und der Stack in den DevTools Performance ist voller React-Internals.
So behebst du es
- Verschiebe den State nach unten: halte ihn in der kleinsten Komponente, die ihn braucht, statt in einer Komponente auf Seitenebene oder einem Context.
- Verpacke teure Teilbäume in React.memo und stabilisiere die übergebenen Props mit useCallback/useMemo — memo ist nutzlos, wenn bei jedem Render ein neuer Callback erzeugt wird.
- Markiere nicht dringende Updates (eine Liste während des Tippens filtern) mit useTransition, damit der dringende Render (das Input selbst) zuerst gezeichnet wird.
Problem
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)} />
</>
}
Lösung
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} />
</>
}
Scanne deine Website kostenlos →