Всегда ли достаточно useCallback и useMemo для перерендеринга?
Хуки
Нет, не всегда. useCallback и useMemo сами по себе не предотвращают ни одного перерендера — они лишь стабилизируют ссылки. Работают только в связке с теми, кто эти ссылки сравнивает.
Когда их недостаточно
Нет React.memo на ребёнке. Стабильный колбэк ничего не даёт, если ребёнок не мемоизирован — он перерендерится вместе с родителем в любом случае:
const handleClick = useCallback(() => {}, []);
return <Child onClick={handleClick} />; // Child без memo — рендерится всегдаНестабильный проп рядом. Один немемоизированный объект/инлайн-JSX ломает всю цепочку:
<MemoChild onClick={stableCb} style={{ margin: 8 }} /> // style новый каждый рендерchildren. JSX-дети — новые объекты каждый рендер; React.memo с children почти всегда бесполезен без отдельной мемоизации.
Перерендер не из-за props. Собственный state ребёнка, useContext, useSelector — memo от них не защищает: контекст будит потребителей напрямую.
Причина выше по дереву. Часто проблема в структуре: часто меняющееся состояние живёт слишком высоко, и мемоизировать приходится всё. Спуск состояния вниз или children-как-проп решают без единого useMemo.
Чем дополнять
- React.memo на дорогих детях (это он пропускает рендер, хуки лишь его «кормят»).
- Правильная структура состояния и разделение контекстов.
- Для тяжёлых обновлений — useTransition/useDeferredValue.
- Для списков — виртуализация: мемоизация не спасёт от тысячи смонтированных строк.
Коротко для собеседования
useCallback/useMemo лишь стабилизируют ссылки — перерендеры пропускает React.memo, и цепочка ломается от одного нестабильного пропа (включая children). Не помогают против обновлений из state/контекста/стора и не заменяют правильную структуру состояния, transitions и виртуализацию.