Какие особенности зависимостей useMemo важно учитывать на практике и почему?
Хуки
Зависимости useMemo решают, когда пересчитывать закешированное значение. Особенности те же, что у остальных хуков, но последствия ошибок специфичны.
Сравнение поверхностное (Object.is)
Примитивы — по значению; объекты, массивы, функции — по ссылке. Отсюда:
const filters = { status }; // новая ссылка
const rows = useMemo(() => filter(data, filters), [data, filters]); // пересчёт всегдаПравильно — зависеть от примитивов ([data, status]) или стабилизировать объект выше.
Полнота: иначе устаревшее значение
Пропущенная зависимость даёт «застывший» результат: список не перефильтруется при смене критерия. Это баг корректности, а не производительности — опаснее лишнего пересчёта.
Мутации невидимы
data.push(x) не меняет ссылку — useMemo вернёт старый кеш. Обновления должны быть иммутабельными, иначе мемоизация «врёт».
useMemo — подсказка, не гарантия
React вправе выбросить кеш (документировано). Нельзя строить на useMemo логику «выполнится ровно один раз»: инициализация синглтонов — useRef или ленивый useState. Побочные эффекты в фабрике недопустимы: она вызывается во время рендера.
Стоимость самих зависимостей
Сравнение deps и хранение значения не бесплатны. Для дешёвых вычислений useMemo вреден: код сложнее, выигрыша нет. Смысл появляется при тяжёлых вычислениях или когда нужна стабильная ссылка (props memo-детей, deps эффектов).
Коротко для собеседования
Deps useMemo сравниваются по Object.is: объекты/функции — по ссылке (нужны примитивы или стабилизация), мутации не видны (только иммутабельность), список должен быть полным (иначе устаревшее значение). useMemo — подсказка, а не гарантия: без побочных эффектов и не для «выполнить один раз».