Перейти к содержанию
  • Лента
  • Категории
  • Последние
  • Метки
  • Популярные
  • Пользователи
  • Группы
Свернуть
exlends
Категории
  1. Главная
  2. Категории
  3. Языки программирования
  4. JavaScript
  5. Почему useCallback не спасает от ре-рендеров в memo-компонентах

Почему useCallback не спасает от ре-рендеров в memo-компонентах

Запланировано Прикреплена Закрыта Перенесена JavaScript
usecallbackре-рендерыreact.memo
1 Сообщения 1 Постеры 5 Просмотры
  • Сначала старые
  • Сначала новые
  • По количеству голосов
Ответить
  • Ответить, создав новую тему
Авторизуйтесь, чтобы ответить
Эта тема была удалена. Только пользователи с правом управления темами могут её видеть.
  • hannadevH Не в сети
    hannadevH Не в сети
    hannadev
    написал отредактировано
    #1

    Junior’ы часто думают, что useCallback - это волшебная палочка против ре-рендеров. Обернул функцию, и memo-компонент больше не дергается. На деле это всего лишь маскировка: функция стабилизируется, но ребенок все равно рендерится из-за inline-объектов или кривых зависимостей.

    Разберем, почему так происходит. Ты увидишь реальные примеры, где useCallback бесполезен, и поймешь, где копать глубже. Это сэкономит часы дебага и избавит от иллюзий про ‘оптимизированный’ код.

    Как работает useCallback под капотом

    useCallback кэширует функцию, сравнивая зависимости по shallow equal. Если массив deps не изменился - возвращает старую функцию. Звучит круто, но React.memo тоже использует shallow compare для пропсов. Функция стабилизируется, но если рядом inline-объект или массив - пропсы не совпадут, и рендер полетит.

    Представь: родительский компонент рендерится из-за setState. Он создает новый объект { onClick, data: [1,2,3] } каждый раз inline. memo-дитя видит новые пропсы - shallow compare проваливается. useCallback на onClick тут как пластырь на перелом: функция старая, но объект новый - ре-рендер неизбежен.

    Ключевые проблемы с зависимостями:

    • Зависимости мутируют - React не видит изменений в объектах, если ссылка та же.
    • Inline-объекты создаются заново при каждом рендере.
    • Контекст или пропсы от родителя ломают всю схему.
    Ситуация useCallback помогает? Почему рендерится
    Inline функция Да -
    Inline объект в пропсах Нет Shallow compare пропсов
    Мутация deps Нет Ссылка не меняется
    React.memo без deps Нет Родитель рендерит детей по умолчанию

    Почему memo-компоненты рендерятся несмотря на useCallback

    memo оборачивает компонент, пропуская рендер, если пропсы shallow equal. useCallback стабилизирует только функцию, но не трогает объекты или массивы в пропсах. Родитель дернулся - передал { handler: useCallback(fn), items: } - новый массив каждый раз, memo срабатывает на рендер.

    Пример: список пользователей. Родитель имеет state users = [{id:1, name:‘Bob’}]. Передаем в ChildList: props = { users, onDelete: useCallback(deleteUser, [users]) }. users меняется - deps меняются - новая функция. Даже если deps пустые, inline slice() или map создаст новый массив.

    Типичные грабли:

    • Забытые deps - eslint-plugin-react-hooks ругается зря, функция захватывает замыкания.
    • Inline объекты - { id, label: name } вместо стабильного объекта из useMemo.
    • Массивы из map без memo - каждый рендер новый массив ссылок.
    // Плохо
    const Child = ({ items, onClick }) => <ul>{items.map(item => <li key={item.id} onClick={() => onClick(item)} />)}</ul>;
    // items новый массив -> ре-рендер
    

    Утечки через неоптимальные зависимости

    Зависимости в useCallback - это shallow compare. Объект dep1 = {a:1} не изменится по ссылке, даже если внутри a поменялось. React подумает, что deps stable - вернет старую функцию. Но в замыкании fn захватит старые данные - баги в runtime.

    Реальный кейс: фильтр списка. deps = [filterObj], filterObj мутируется setFilter({ …filterObj, value: newVal }). Ссылка та же - useCallback вернет старую fn с устаревшим замыканием. Дитя получит stable fn, но данные черствые.

    Как фиксить:

    1. Хуки для объектов - useMemo для стабильных пропсов.
    2. Дроби deps на примитивы - id вместо объекта.
    3. Custom comparer для memo - React.memo с areEqual функцией.
    deps Shallow equal? Проблема
    Примитивы Да -
    Новый объект Нет Ре-рендер
    Мутация объекта Да Устаревшее замыкание
    useMemo объект Да Стабильно

    Маскировка вместо рефакторинга

    useCallback часто юзают как костыль: ‘рендерится - добавим callback’. Вместо фикса архитектуры - state lift или Context - лепят мемо. Производительность маскируется, но бандл растет, дебажить сложнее.

    Под капотом: ивент-луп не блокируется, React батчит рендеры, но лишние diff’ы жрут CPU. Junior радуется React DevTools Profiler - ‘зеленые блоки!’ - не зная, что под водой утечки.

    Факты начистоту:

    • useCallback + memo спасает только shallow пропсы.
    • Более 70% ре-рендеров от inline объектов (по бенчмаркам).
    • Лучше рефакторить: выноси state в reducer, используй compound components.

    Рефакторинг вместо иллюзий оптимизации

    useCallback - не панацея, а инструмент в арсенале. Маскирует симптомы, но не лечит: inline-объекты и кривые deps остаются. Подумай о структуре: зачем Child зависит от всего массива? Раздели на маппинг с ключами, stable handlers через id.

    Осталось за кадром: custom hooks для батчинга пропсов или Reselect-подобные селекторы. Если копнешь в scheduler - увидишь, почему даже memo не всегда спасает от parent re-renders. В следующий раз разберем, как ивент-луп рвет твои оптимизации.

    1 ответ Последний ответ
    0

    Здравствуйте! Похоже, вас заинтересовала эта беседа, но у вас ещё нет аккаунта.

    Надоело каждый раз пролистывать одни и те же посты? Зарегистрировав аккаунт, вы всегда будете возвращаться на ту же страницу, где были раньше, и сможете выбирать, получать ли уведомления о новых ответах (по электронной почте или в виде push-уведомлений). Вы также сможете сохранять закладки и ставить лайки постам, чтобы выразить свою благодарность другим участникам сообщества.

    С вашими комментариями этот пост мог бы стать ещё лучше 💗

    Зарегистрироваться Войти

    Категории

    • Главная
    • Новости
    • Фронтенд
    • Бекенд
    • Языки программирования

    Контакты

    • Сотрудничество
    • info@exlends.com

    © 2024 - 2026 ExLends, Inc. Все права защищены.

    Политика конфиденциальности
    • Войти

    • Нет учётной записи? Зарегистрироваться

    • Войдите или зарегистрируйтесь для поиска.
    • Первое сообщение
      Последнее сообщение
    0
    • Лента
    • Категории
    • Последние
    • Метки
    • Популярные
    • Пользователи
    • Группы