Каскад ре-рендерів React: коли один клік перемальовує все дерево
Що відбувається
У React оновлення стану перемальовує компонент і, за замовчуванням, усіх його дітей. Якщо стан живе високо в дереві (контекст, стор рівня сторінки), одне натискання клавіші чи клік може перемалювати сотні компонентів — більшість із яких показують те саме, що й раніше.
Один рендер дешевий; тисячі рендерів в одному обробнику — ні. Ця робота потрапляє у фазу processing time в INP.
Як розпізнати
- Профайлер React DevTools показує широкі flame-графи: компоненти перемальовуються без видимої зміни їхніх props.
- Введення в controlled input лагає; кожне натискання клавіші перемальовує великі списки чи таблиці.
- Processing time домінує в INP, а стек у DevTools Performance повний внутрішнощів React.
Як виправити
- Опустіть стан униз: тримайте його в найменшому компоненті, якому він потрібен, а не в компоненті рівня сторінки чи контексті.
- Оберніть дорогі піддерева в React.memo та стабілізуйте передані їм props через useCallback/useMemo — memo марний, якщо на кожен рендер створюється новий колбек.
- Позначайте нетермінові оновлення (фільтрація списку під час введення) через useTransition, щоб терміновий рендер (сам input) відмалювався першим.
Проблема
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)} />
</>
}
Виправлення
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} />
</>
}
Просканувати свій сайт безкоштовно →