В чем разница между useMemo и react.memo и useCallback

Ответ

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 — кэширует целый компонент, предотвращает ререндер, если пропсы те же.

Вот и вся магия, ёпта. Главное — не начинать это всё юзать везде подряд, а то можно так заоптимизироваться, что сам потом не разберёшься. Сначала профилируй, находи узкие места, а потом уже прикладывай эти инструменты, как таблетку от конкретной болезни.