useMemo, useCallback та React.memo у React: Коли їх варто використовувати

React

Оптимізація продуктивності — це одна з ключових задач для розробників React-додатків. Інструменти useMemo, useCallback і React.memo допомагають уникнути зайвих рендерів компонентів та економити ресурси браузера. Розглянемо, як і коли доцільно їх використовувати.

Що таке useMemo?

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

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

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

import { useMemo } from 'react';

function ExpensiveComponent({ num }) {
  const result = useMemo(() => {
    console.log('Обчислення...');
    return num ** 2;
  }, [num]);

  return <div>Результат: {result}</div>;
}

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

  • Коли обчислення складні або залежать від великого обсягу даних.
  • Коли рендеринг без useMemo призводить до значного зниження продуктивності.

Що таке useCallback?

useCallback кешує функції, щоб їхні посилання не змінювалися між рендерами.

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

Використання в парі з дочірнім компонентом:

import { useState, useCallback } from 'react';

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

  const handleClick = useCallback(() => {
    setCount(prevCount => prevCount + 1);
  }, []);

  return (
    <div>
      <Child onClick={handleClick} />
      <p>Клікнуто {count} разів</p>
    </div>
  );
}

function Child({ onClick }) {
  console.log('Child рендериться');
  return <button onClick={onClick}>Клікни мене</button>;
}

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

  • Коли функції передаються як пропси в дочірні компоненти.
  • У поєднанні з React.memo для уникнення зайвих рендерів.

Що таке React.memo?

React.memo — це HOC (Higher-Order Component), який запобігає повторному рендеру компонента, якщо його пропси не змінилися.

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

import React from 'react';

const MemoizedChild = React.memo(function Child({ value }) {
  console.log('Child рендериться');
  return <div>{value}</div>;
});

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

  return (
    <div>
      <MemoizedChild value="Статичний текст" />
      <button onClick={() => setCount(count + 1)}>Збільшити</button>
    </div>
  );
}

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

  • Для компонентів, які рідко змінюють свої пропси.
  • Коли уникнення зайвого рендеру дає помітний приріст продуктивності.

Взаємодія між useMemo, useCallback та React.memo

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

  1. useCallback створює кешовану функцію.
  2. useMemo оптимізує обчислення.
  3. React.memo зменшує кількість рендерів компонентів.

Комбінований приклад

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

  const expensiveCalculation = useMemo(() => {
    console.log('Виконуємо обчислення...');
    return count ** 2;
  }, [count]);

  const handleClick = useCallback(() => {
    setCount(prevCount => prevCount + 1);
  }, []);

  return (
    <div>
      <MemoizedChild value={expensiveCalculation} onClick={handleClick} />
    </div>
  );
}

const MemoizedChild = React.memo(({ value, onClick }) => {
  console.log('Child рендериться');
  return <div onClick={onClick}>Результат: {value}</div>;
});

Коли не використовувати ці інструменти?

  • useMemo та useCallback: Якщо кешування дає більше витрат на пам’ять, ніж виграш у продуктивності.
  • React.memo: Якщо компонент рендериться швидко і без оптимізації.

Висновок

Оптимізація React-додатків з useMemo, useCallback та React.memo може значно покращити продуктивність, особливо у складних інтерфейсах. Проте їхнє використання має бути обґрунтованим: надмірна оптимізація може ускладнити код і не дати значного ефекту.