В React-компоненте: function Parent(){ const [c, setC] = useState(0); return setC(c+1)} /> } — объясните, почему это может вызывать лишние рендеры дочерних компонентов и предложите варианты оптимизации

23 Авг в 04:12
16 +1
0
Ответы
1
Проблема
- Строка `onClick={() => setC(c+1)}` создаёт новую функцию при каждом рендере Parent. Даже если Child обёрнут в `React.memo`, сравнение пропсов — поверхностное (по ссылке), и новая функция — это другое значение, поэтому Child будет ререндериться лишний раз.
- Корень: функция — не примитив, её identity меняется при каждом рендере родителя.
Почему это важно (коротко)
- При изменении `c` (например c= ⁣0\,c=\!0c=0 → увеличиваем на 1\,11) Parent ререндерится; при каждом рендере создаётся новая callback-функция → Child получает «новый» проп → ререндер Child.
Варианты оптимизации (с примерами)
1) Мемозировать обработчик через useCallback + функциональное обновление:
- Почему: можно сделать callback стабильным по ссылке и не зависеть от текущего `c`.
- Пример:
const handleClick = useCallback(() => {
setC(prev => prev + 1);
}, []);
return ;
Здесь инкремент делаем как функциональное обновление `prev => prev + 1`, поэтому useCallback не зависит от `c` и остаётся стабильным.
2) Мемонизация Child:
- Обернуть Child в `React.memo(Child)` (или реализовать shouldComponentUpdate/ PureComponent в классе). Это полезно вместе с пунктом (1), но само по себе не поможет, если проп-функция постоянно меняется.
3) Передавать стабильную функцию/диспетчер:
- React гарантирует стабильность identity сеттеров setState, поэтому можно передавать `setC` как диспетчер и вызывать его в дочернем с функциональным обновлением:
// в Child: props.onIncrement(prev => prev + 1)
- Или использовать useReducer — dispatch тоже стабилен по ссылке.
4) Хранить callback в ref (редкий кейс):
- Если нужно совсем избежать пересоздания и не хотите useCallback, можно положить функцию в ref и вызывать `ref.current()`. Обычно сложнее и менее читаемо.
5) Не оптимизировать преждевременно:
- Если Child дешёвый или Parent часто меняет другие пропсы, оптимизация бессмысленна. Измеряйте (Profiler) перед оптимизацией.
Короткая рекомендация
- Для вашего примера чаще всего достаточно: обернуть Child в `React.memo` и в Parent использовать
const handleClick = useCallback(() => setC(prev => prev + 1), []);
return ;
Тогда Child не будет ререндериться из‑за новой функции при каждом рендере Parent.
(Замечание про числа: инкремент — +1\,+1+1; начальное состояние в примере — 0\,00.)
23 Авг в 04:17
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир