useMemo() — это ловушка, используемая в функциональном компоненте реакции, которая возвращает запомненное значение.

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

Запоминаемая функция запоминает результаты вывода для заданного набора входных данных.

Например, если есть функция для сложения двух чисел, и мы задаем параметр как 10 и 20 в первый раз, функция сложит эти два числа и вернет 30, но если те же входные данные поступят снова, мы вернем кешированный значение, т.е. 30, и больше не вычислять с помощью функции добавления.

В реакции мы также используем эту концепцию, всякий раз, когда в компоненте React состояние и реквизиты не изменяют компонент, и компонент не перерисовывается, он показывает тот же результат.

Хук useMemo используется для повышения производительности в нашем приложении React.

const memoizedValue = useMemo(functionThatReturnsValue, 
                                   arrayDepencies)

Без использованияMemo()

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

В этом примере у нас есть функция расчета, которая запускается при каждом рендеринге.

При изменении счетчика или добавлении задачи вы заметите задержку в выполнении.

import { useState } from "react";
import ReactDOM from "react-dom/client";

const App = () => {
  const [count, setCount] = useState(0);
  const [todo, setTodo] = useState([]);
  const calculation = calculation(count);

  const increment = () => {
    setCount((c) => c + 1);
  };
  const addTodo = () => {
    setTodo((t) => [...t, "New Todo"]);
  };
  const calculation = (num) => {
    console.log("Calculating...");
    for (let i = 0; i < 1000000000; i++) {
      num += 1;
    }
    return num;
  };
  return (
    <div>
      <div>
        <h2>My Todos</h2>
        {todo.map((todo, index) => {
          return <p key={index}>{todo}</p>;
        })}
        <button onClick={addTodo}>Add Todo</button>
      </div>
      <hr />
      <div>
        Count: {count}
        <button onClick={increment}>+</button>
        <h2>Calculation</h2>
        {calculation}
      </div>
    </div>
  );
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

С использованиемMemo()

Чтобы решить эту проблему с производительностью, мы можем использовать хук useMemo, чтобы запомнить функцию calculation. Это приведет к тому, что функция будет работать только тогда, когда это необходимо.

Мы можем обернуть вызов функции вычисления с помощью useMemo.

useMemoHook принимает второй параметр для объявления зависимостей. Функция вычисления будет работать только тогда, когда ее зависимости изменились.

В следующем примере функция вычисления будет выполняться только при изменении count, а не при добавлении задач.

import { useState, useMemo } from "react";
import ReactDOM from "react-dom/client";

const App = () => {
  const [count, setCount] = useState(0);
  const [todos, setTodos] = useState([]);
  const calculation = useMemo(() => calculation(count), [count]);

  const increment = () => {
    setCount((c) => c + 1);
  };
  const addTodo = () => {
    setTodos((t) => [...t, "New Todo"]);
  };
  const calculation = (num) => {
    console.log("Calculating...");
    for (let i = 0; i < 1000000000; i++) {
      num += 1;
    }
    return num;
  };
  return (
    <div>
      <div>
        <h2>My Todos</h2>
        {todos.map((todo, index) => {
          return <p key={index}>{todo}</p>;
        })}
        <button onClick={addTodo}>Add Todo</button>
      </div>
      <hr />
      <div>
        Count: {count}
        <button onClick={increment}>+</button>
        <h2>Calculation</h2>
        {calculation}
      </div>
    </div>
  );
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);