Каковы основные различия между React.memo() и useMemo()?

Оптимизация производительности является важным фактором в веб-разработке. React использует «запоминание» в качестве метода оптимизации для ускорения процесса рендеринга компонентов. Он предлагает React.memo() и useMemo() для реализации мемоизации.

В этой статье будут сравниваться и противопоставляться React.memo и useMemo(), а также обсуждаться варианты их использования.

Необходимость React.memo() и useMemo()

Лучший способ понять, зачем нам нужны React.memo() и useMemo(), — это посмотреть, как React перерисовывает компоненты без мемоизации.

Для этого рассмотрим простой пример с двумя компонентами React. Первый компонент является родительским компонентом. У него есть кнопка, которая увеличивает значение переменной count.

import Child from "./Child";
import "./styles.css";
import React from "react";
import { useState} from "react"; 

//Parent.js
export default function Parent() { 
const [count, setCount] = useState(0); 
const handleClick = () => { setCount(count + 1); }; 
console.log("Parent Render"); 

return ( 
<div> 
  <button onClick={handleClick}>Increase Count</button> 
  <h2>{count}</h2> 
  <Child name={"Child Component"} /> 
</div> 
  );
}

Второй компонент является дочерним компонентом и отображает имя, переданное от родительского компонента.

import React from "react"; 

//Child.js
export default function Child(props) { 
  console.log("Child Render"); 
  return ( 
    <div> 
      <h2>{props.name}</h2> 
    </div> 
  );
}

Как только пользователь нажмет кнопку Increase Count, счетчик увеличится, что приведет к повторному рендерингу в родительском компоненте. Здесь изменение значения переменной count не влияет на свойства, переданные в дочернюю переменную, и дочерний компонент не должен повторно отображаться.

Однако, как видите, метод рендеринга дочернего компонента также вызывается всякий раз, когда родительский компонент повторно визуализируется. Это заставит виртуальный DOM дочерних компонентов выполнить проверку на отклонение от предыдущего состояния виртуального DOM. Но настоящий DOM не изменится, потому что дочерний компонент не имеет изменений.

Хотя реальный DOM не меняется, потребуется некоторое время, чтобы сравнить его с виртуальным DOM, чтобы увидеть идентичные. Таким образом, такое поведение может вызвать серьезные проблемы с производительностью и увеличить время загрузки крупномасштабных приложений.

Вот почему нам нужно использовать React.memo() и useMemo() для оптимизации процесса рендеринга компонента React.

Что такое React.memo()?

React.memo() был введен в React 16.6, чтобы избежать ненужного повторного рендеринга в функциональных компонентах. Это компонент более высокого порядка, который принимает другой компонент в качестве реквизита. Он будет отображать компонент только в том случае, если есть какие-либо изменения в свойствах.

Давайте теперь снова рассмотрим приведенный выше пример, чтобы понять, как работает React.memo(). Но на этот раз нам нужно обернуть дочерний компонент React.memo().

import React from "react"; 

//Child.jsexport 
function Child(props) { 
  console.log("Child Render"); 
  return ( 
    <div> 
      <h2>{props.name}</h2> 
    </div> 
  );
} 

export default React.memo(Child); //Add React.memo() HOC to the child component

Когда мы нажимаем кнопку Increase Count, дочерний компонент не будет повторно визуализирован, и приложение будет повторно использовать предыдущий визуализированный вывод.

Важно помнить, что React.memo() будет проверять только изменения реквизита. Если функциональный компонент имеет хук useState, useReducer или useContext, он все равно вызовет повторную визуализацию при изменении состояния или контекста.

Кроме того, React.memo() будет поверхностно сравнивать сложные объекты в объекте реквизита. Если вам нужно больше полномочий для сравнения, вы также можете передать пользовательскую функцию сравнения в качестве второго аргумента, как показано ниже:

export function Child(props) { 
  /* render using props */
 }
function areSame(prevProps, nextProps) { 
    /* return true if passing nextProps to render would return the same result as passing prevProps to render, otherwise return false */
 }

export default React.memo(Child, areSame);

Когда следует использовать React.memo()?

Лучше всего использовать React.memo(), когда:

  • Компоненты отображаются слишком часто и замедляют работу приложения.
  • Стоимость рендеринга компонентов высока (при времени загрузки более 100 мс).
  • Компонент продолжает повторно отображать один и тот же набор реквизитов.

Однако использование React.memo() в облегченных компонентах может вызвать снижение производительности и непредвиденные ошибки. Таким образом, мы должны также думать о сложности компонента при использовании React.memo().

Что такое useMemo()?

useMemo() — один из наиболее часто используемых хуков React среди разработчиков. Он принимает функцию и массив зависимостей в качестве входных данных и запоминает значение, возвращаемое этой функцией.

Особенность useMemo() заключается в том, что он будет пересчитывать запомненное значение только при изменении одной из зависимостей. Эта оптимизация помогает избежать дорогостоящих расчетов при каждом рендеринге.

Давайте теперь обратимся к примеру, чтобы понять, как работает useMemo().

import Child from "./Child";
import React from "react";
import { useState, useRef, useMemo} from "react";

//Parent.js
export default function Parent() {
  const [counts, setCounts] = useState(0);
  const [times, setTimes] = useState(0);
  const handleClick = () => { 
      setCounts(counts + 1); 
  };

  const useMemoRef = useRef(0);
  const incrementUseMemoRef = () => useMemoRef.current++;
  const memoizedValue = useMemo(() => incrementUseMemoRef(), [times]);

  console.log("Parent Render");

return (

  <div>
    <button onClick={() => setTimes(times + 1)}>Force Child Render</button>
    <br></br>
    <br></br>
    <button onClick={handleClick}>Increase Count</button>
    <Child memoizedValue={memoizedValue} />
  </div>
  )
 }

Это показывает, что в родительском компоненте мы используем useRef()Hook для отслеживания количества повторных рендеров дочернего компонента. Ловушка useMemo() вызывает функцию incrementUseMemoRef, а значение, возвращаемое хуком useMemo(), затем сохраняется в переменной memoizedValue. Это увеличивает значение нашего useMemoRef.current на единицу каждый раз, когда обновляются зависимости. Вы увидите, что если вы нажмете кнопку Increase Count, memoizedValue не обновится.

import React from "react";

//Child.js
function Child({memoizedValue}) {
  console.log("Child Render");

  return (
    <div> 
      <h2>Child Component</h2>  
      <p>
        I'll only re-render when you click <b>Force Child Render.</b>
     </p>
   </div>
 )
}

export default React.memo(Child);

Дочерний компонент будет повторно визуализирован только при нажатии кнопки Force Child Render, так как memoizedValue обновляется.

Когда использовать useMemo?

Перед использованием хука useMemo() необходимо убедиться, что:

  • Вы проанализировали компонент и проверили, вычисляет ли он стоимость при каждом рендеринге.
  • Поскольку он выполняется во время рендеринга, ваш хук useMemo() не имеет побочных эффектов, и все побочные эффекты находятся в хуке useEffect().
  • Вы не нарушаете никаких правил и стандартов React Hooks.

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

Это похоже на сохранение ответа функции в переменной, а затем обращение к значению ответа из переменной без выполнения функции каждый раз.

Следует ли вам использовать React.memo() или useMemo()?

Выбор между React.memo() и useMemo() должен быть простым. Теперь вы хорошо понимаете их обоих.

  • Используйте React.memo, чтобы запомнить весь компонент.
  • Используйте useMemo для запоминания значения внутри функционального компонента.

Создавайте компонуемые веб-приложения

Не создавайте веб-монолиты. Используйте Bit для создания и компоновки несвязанных программных компонентов — в ваших любимых фреймворках, таких как React или Node. Создавайте масштабируемые и модульные приложения с мощными и приятными возможностями разработки.

Перенесите свою команду в Bit Cloud, чтобы совместно размещать и совместно работать над компонентами, а также значительно ускорить, масштабировать и стандартизировать разработку в команде. Начните с компонуемых интерфейсов, таких как Design System или Micro Frontends, или исследуйте компонуемый сервер. Попробуйте →

Узнать больше