Реальна різниця між useMemo та React.memo в React

React

У світі React оптимізація продуктивності компонентів є однією з найважливіших задач. Два популярні інструменти для цього — це хук useMemo і вищий орієнтований компонент React.memo. Вони часто викликають плутанину, адже обидва працюють з продуктивністю, але мають зовсім різні сфери застосування. У цій статті розглянемо, чим саме відрізняються useMemo та React.memo, коли їх варто використовувати і як уникнути поширених помилок.

Що таке useMemo?

Основна ідея

useMemo — це хук, який використовується для мемоізації значень. Він обчислює значення та кешує його, доки залишається незмінною залежність. Це корисно, коли у вас є дорогі обчислення, які не потрібно виконувати при кожному рендері компонента.

Синтаксис

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

Як працює

  • Функція, передана в useMemo, виконується лише тоді, коли змінюються залежності (у цьому випадку a або b).
  • Якщо залежності не змінюються, useMemo повертає кешоване значення.

Приклад використання

import React, { useState, useMemo } from 'react';

function ExpensiveComponent({ items }) {
  const expensiveCalculation = (arr) => {
    console.log('Виконуємо обчислення...');
    return arr.reduce((sum, item) => sum + item, 0);
  };

  const [count, setCount] = useState(0);

  const total = useMemo(() => expensiveCalculation(items), [items]);

  return (
    <div>
      <h1>Сума: {total}</h1>
      <button onClick={() => setCount(count + 1)}>Клікнуто {count} разів</button>
    </div>
  );
}

Результат: Обчислення виконується лише тоді, коли змінюється масив items. Клік на кнопку не викликає повторних обчислень.

Що таке React.memo?

Основна ідея

React.memo — це вищий орієнтований компонент (HOC), який запобігає зайвим рендерам компонента, якщо його пропси не змінилися. Він працює шляхом порівняння старих і нових пропсів.

Синтаксис

const MemoizedComponent = React.memo(MyComponent);

Як працює

  • React.memo автоматично використовує поверхневе порівняння пропсів.
  • Якщо пропси не змінюються, компонент не перерендерюється.

Приклад використання

import React from 'react';

const MyComponent = ({ value }) => {
  console.log('Рендеримо компонент');
  return <div>{value}</div>;
};

const MemoizedComponent = React.memo(MyComponent);

function App() {
  return (
    <div>
      <MemoizedComponent value="React.memo" />
    </div>
  );
}

Результат: Компонент MyComponent не буде перерендерюватися, якщо проп value не змінюється.

Головні відмінності між useMemo та React.memo

Характеристика useMemo React.memo
Призначення Мемоізує значення (обчислення, функції). Запобігає зайвим рендерам компонента.
Де використовується У функціональних компонентах. У функціональних компонентах або їх експорті.
Що оптимізує Обчислення, які залежать від залежностей. Рендеринг компонентів із незмінними пропсами.
Сфера застосування Для важких обчислень або великих даних. Для оптимізації роботи дочірніх компонентів.
API Хук (використовується всередині компонентів). Вищий орієнтований компонент (HOC).

Коли використовувати useMemo?

  1. Важкі обчислення
    Якщо у вас є обчислення, яке займає багато часу, і результат залежить лише від певних параметрів, використовуйте useMemo.

    Приклад: Підрахунок суми чисел, фільтрація чи сортування великих масивів.

  2. Мемоізація функцій
    Хоча для функцій краще підходить useCallback, useMemo також може використовуватися для мемоізації функцій.

    Приклад:

    const memoizedFunction = useMemo(() => () => console.log('Hello'), []);
    

Коли використовувати React.memo?

  1. Дочірні компоненти
    Якщо компонент отримує пропси, які змінюються рідко, варто використовувати React.memo.
  2. Списки та їхні елементи
    Для великих списків можна мемоізувати кожен елемент за допомогою React.memo, щоб уникнути рендерингу незмінних елементів.

    Приклад:

    const ListItem = React.memo(({ item }) => {
      console.log('Рендеримо елемент', item);
      return <li>{item}</li>;
    });
    
  3. Кастомне порівняння пропсів
    Якщо потрібно порівнювати пропси вручну, можна передати функцію areEqual як другий аргумент:

    const MemoizedComponent = React.memo(MyComponent, (prevProps, nextProps) => {
      return prevProps.value === nextProps.value;
    });
    

Поширені помилки

  1. Надмірне використання
    Використання useMemo або React.memo там, де це не потрібно, може ускладнити код без значного виграшу в продуктивності.
  2. Ігнорування залежностей
    Неправильне вказування або пропуск залежностей у useMemo може призвести до непередбачуваної поведінки.

    Приклад помилки:

    const result = useMemo(() => computeValue(a, b), []); // Неправильно: залежності відсутні
    
  3. Неочікувана зміна пропсів
    Якщо пропси є об’єктами чи функціями, вони можуть змінюватися через зміну посилань. Використовуйте useCallback або useMemo для стабілізації.

Поєднання useMemo та React.memo

Ці інструменти часто використовуються разом для забезпечення оптимальної продуктивності:

const ChildComponent = React.memo(({ value }) => {
  console.log('Рендеримо компонент');
  return <div>{value}</div>;
});

function ParentComponent() {
  const [count, setCount] = useState(0);

  const memoizedValue = useMemo(() => `Value: ${count}`, [count]);

  return (
    <div>
      <ChildComponent value={memoizedValue} />
      <button onClick={() => setCount(count + 1)}>Збільшити</button>
    </div>
  );
}

Висновок

  • Використовуйте useMemo для мемоізації значень та важких обчислень, які залежать від змінних.
  • Використовуйте React.memo для запобігання зайвим рендерам компонентів із незмінними пропсами.
  • Завжди пам’ятайте, що надмірна оптимізація може зробити код складнішим без значного приросту продуктивності. Застосовуйте ці інструменти лише там, де це справді необхідно.

Дотримуючись цих принципів, ви зможете створювати продуктивніші та масштабованіші React-додатки! 🚀