Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Передача callback-функцій». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Зайві перерендери через колбеки виникають тоді, коли дочірній компонент обгорнутий у `React.memo`, а посилання на функцію-prop змінюється при кожному рендері батька.** Лікується стабільною ідентичністю функції та акуратною мемоізацією. **Ключове:** без `React.memo` у дитини стабільність колбека не врятує - вона і так перерендериться разом із батьком.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Зайві перерендери через колбеки виникають тоді, коли дочірній компонент обгорнутий у** `React.memo`**, а посилання на функцію-prop змінюється при кожному рендері батька.** Лікується стабільною ідентичністю функції та акуратною мемоізацією. --- ## Що робити 1. **Роби колбеки стабільними (**`useCallback`**)** ```javascript const handleSelect = useCallback((id: string) => { setSelected(id); // можна через функціональний апдейт }, []); // залежності тільки ті, що реально потрібні ``` > Стабільне посилання -> менше шансів "розбудити" дітей `React.memo`. 2. **Мемоізуй дитину (**`React.memo`**)** ```javascript const Item = React.memo(function Item({ onSelect, item }) { return <button onClick={() => onSelect(item.id)}>{item.title}</button>; }); ``` > Без `React.memo` у дитини стабільність колбека не врятує - вона і так перерендериться разом із батьком. 3. **Уникай "фабрик" функцій у JSX** ```javascript // bad: щоразу нова функція {items.map(i => <Item key={i.id} onSelect={() => onSelect(i.id)} item={i} />)} // good: одна стабільна функція + дані йдуть окремим пропом const onSelect = useCallback((id: string) => { /* ... */ }, []); {items.map(i => <Item key={i.id} onSelect={onSelect} item={i} />)} ``` 4. **Використовуй функціональні оновлення, щоб не тягнути state у залежності** ```javascript // bad: тягне selected у deps -> ламає стабільність const onToggle = useCallback(() => setSelected(selected + 1), [selected]); // good: посилання стабільне const onToggle = useCallback(() => setSelected(s => s + 1), []); ``` 5. **Коли потрібен "останній" стейт, але стабільне посилання - патерн useEvent (через ref)** ```javascript function useEvent<T extends (...a: any[]) => any>(fn: T): T { const ref = useRef(fn); useLayoutEffect(() => { ref.current = fn; }); return useCallback(((...args) => ref.current(...args)) as T, []); } // Використання: const onChange = useEvent((value: string) => { // читає найсвіжіший state/props, але посилання на колбек незмінне }); ``` 6. **Не передавай щоразу нові об'єкти з колбеками** ```javascript // bad: options новий -> будить дитину <Chart options={{ onHover }} /> // good: мемоізуй об'єкт-проп const chartOptions = useMemo(() => ({ onHover }), [onHover]); <Chart options={chartOptions} /> ``` 7. **Іноді простіше змінити API пропсів** - Замість `onSelect={() => onSelect(item.id)}` передай `id`, а сам обробник створюй усередині `Item`. - Або передай один `dispatch` (з `useReducer`) - **його посилання стабільне за контрактом**. 8. **Дроби компоненти і піднімай мемоізацію вище** - Винеси "важку" частину в `React.memo`-компонент, щоб навіть при зміні колбека перемальовувався тільки маленький вузол. --- ## Чого не варто робити (поширені пастки) - **Ставити** `useCallback` **всюди.** Він теж коштує трохи CPU. Став там, де: 1. дитина мемоізована (`React.memo`/`memo`), **і** 2. функція реально йде їй у пропси. - **Класти в залежності все підряд.** Тримай масив deps мінімальним і коректним; для стейту - частіше функціональні апдейти. - **Використовувати індекс масиву як** `key` у списках із колбеками - через перестановки втратиш вигоду мемо і стан дітей.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.