Ответ
useMemo кэширует результат вычислений и пересчитывает его только при изменении зависимостей. Используется для оптимизации дорогих вычислений.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
React.memo — HOC, который мемоизирует компонент, предотвращая лишние ререндеры при неизменных пропсах.
const MemoComponent = React.memo(MyComponent);
useCallback кэширует саму функцию, а не её результат. Полезен при передаче колбэков в дочерние компоненты.
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
Ключевые отличия:
useMemo— для значенийuseCallback— для функцийReact.memo— для компонентов
Ответ 18+ 🔞
Ага, слушай, вот эта вся хуйня с мемоизацией в Реакте — она многих в ступор вгоняет, а на деле-то всё просто, как три копейки. Смотри, разжую.
useMemo — это как жадный калькулятор, сука. Он посчитал что-то один раз и запомнил результат. И будет тыкать тебе этот старый результат, пока ты специально не ткнёшь его мордой в то, что входные данные поменялись. Используешь, когда вычисление дорогое, как чугунный мост.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
Вот тут computeExpensiveValue — это твоя тяжёлая функция, которая, например, сортирует овердохуища данных. Она перезапустится только если a или b изменились. А так — будет показывать старое, закэшированное значение. Хуй с горы, оптимизация!
React.memo — это уже не хук, а высший порядок, так сказать, компонентный прибамбас. Он оборачивает твой компонент и говорит: "Стоять, ёпта! Не рендерься просто так!". Компонент перерендерится только если его пропсы реально изменились.
const MemoComponent = React.memo(MyComponent);
Представь, есть у тебя список из 1000 элементов, и данные обновляются только у одного. Без React.memo перерисуются все 1000, а с ним — только один, чей пропс поменялся. Красота, блядь!
useCallback — а вот это хитрая жопа. Он кэширует не результат, а саму функцию, её тушку. Зачем? А затем, что в JavaScript () => {} — это каждый раз новая функция, и для дочернего компонента, обёрнутого в React.memo, это будет каждый раз новый пропс, и он будет перерендериваться по-любому, даже если ничего не поменялось.
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
Вот мы завернули функцию в useCallback, и теперь её ссылка остаётся той же самой, пока не поменяются зависимости [a, b]. Дочерний MemoComponent получит старый пропс и не станет лишний раз дергаться. Удивление пиздец, как всё связано!
Короче, резюмирую, чтобы в голове не еблось:
useMemo— кэширует значение, результат вычислений. Используешь для тяжёлой математики, фильтрации, форматирования.useCallback— кэширует саму функцию, её инстанс. Используешь, когда передаёшь колбэк в оптимизированный дочерний компонент (React.memo).React.memo— кэширует целый компонент, предотвращает ререндер, если пропсы те же.
Вот и вся магия, ёпта. Главное — не начинать это всё юзать везде подряд, а то можно так заоптимизироваться, что сам потом не разберёшься. Сначала профилируй, находи узкие места, а потом уже прикладывай эти инструменты, как таблетку от конкретной болезни.