Примечание. Прежде чем приступить к работе с блогом, важно иметь четкое представление об основных принципах и концепциях, связанных с хуками.

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

Реагировать на оптимизацию производительности

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

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

Что такое useMemo?

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

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

Практический пример без useMemo:

import React, { useState } from "react";

const Counter = () => {
  const [countOne, setCountOne] = useState(0);
  const [countTwo, setCountTwo] = useState(0);

  const incrementCountOne = () => {
    setCountOne(countOne + 1);
  };

  const incrementCountTwo = () => {
    setCountTwo(countTwo + 1);
  };

  const multipleCountOne = () => {
    let i = 0;
    let res = 1;
    /* Perform some heavy operation */
    while (i < 2000000000) {
      i++;
    }
    return res * countOne;
  };

  return (
    <>
      <button onClick={incrementCountOne}>Counter - {countOne}</button>
      <button onClick={incrementCountTwo}>Counter - {countTwo}</button>
      <br />
      <span>{multipleCountOne()}</span>
    </>
  );
};

export default Counter;

В приведенном примере, несмотря на то, что тяжелая операция явно выполняется над CountOne, любые изменения в CountOne вызовут повторную визуализацию компонента, что также повлияет на визуализацию CountTwo.

Практический пример с useMemo:

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

const Memo = () => {
  const [countOne, setCountOne] = useState(0);
  const [countTwo, setCountTwo] = useState(0);

  const incrementCountOne = () => {
    setCountOne(countOne + 1);
  };

  const incrementCountTwo = () => {
    setCountTwo(countTwo + 1);
  };

  const multipleCountOne = useMemo(() => {
    let i = 0;
    let res = 1;
    /* Perform some heavy operation */
    while (i < 2000000000) {
      i++;
    }
    return res * countOne;
  }, [countOne]);

  return (
    <>
      <button onClick={incrementCountOne}>Counter - {countOne}</button>
      <button onClick={incrementCountTwo}>Counter - {countTwo}</button>
      <br />
      <span>{multipleCountOne}</span>
    </>
  );
};

export default Memo;

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

Что такое useCallback?

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

Используя хук useCallback в React, мы можем создавать эффективные обратные вызовы, которые избегают ненужного повторного рендеринга, кэшируя ссылку на функцию, когда зависимости остаются прежними.

Практический пример без useCallback:

//Parent.jsx

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

const Parent = () => {
  const [countOne, setCountOne] = useState(0);
  const [countTwo, setCountTwo] = useState([]);

  const incrementCountOne = () => {
    setCountOne(countOne + 1);
  };

  const printHelloWhenCountTwoChanges = () => {
     console.log("Hello count two");
  };

  return (
    <>
      <Child countTwo={countTwo} printHello={printHelloWhenCountTwoChanges} />
      <button onClick={incrementCountOne}>Counter - {countOne}</button>
    </>
  );
};

export default Parent;
//Child.jsx

import React, { memo } from "react";

const Child = ({ count, printHelloWhenCountTwoChanges }) => {
  console.log("Child component rendered");
  return <div>Child</div>;
};

export default memo(Child);

В данном примере дочерний компонент явно не зависит от состояния «countOne» родительского компонента, он по-прежнему подлежит повторному выполнению при нажатии кнопки. Несмотря на использование методов мемоизации, таких как React’s memo, для предотвращения ненужного выполнения дочернего компонента, он продолжает ненужно отображать из-за повторяющегося вызова метода incrementCountTwo каждый раз при загрузке родительского компонента.

Практический пример с useCallback:

//Parent.jsx

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

const Parent = () => {
  const [countOne, setCountOne] = useState(0);
  const [countTwo, setCountTwo] = useState([]);

  const incrementCountOne = () => {
    setCountOne(countOne + 1);
  };

 const printHelloWhenCountTwoChanges = useCallback(() => {
    console.log("Hello count two");
  }, [countTwo]);

  return (
    <>
      <Child countTwo={countTwo} printHello={printHelloWhenCountTwoChanges} />
      <button onClick={incrementCountOne}>Counter - {countOne}</button>
    </>
  );
};

export default Parent;
//Child.jsx

import React, { memo } from "react";

const Child = ({ count, printHelloWhenCountTwoChanges }) => {
  console.log("Child component rendered");
  return <div>Child</div>;
};

export default memo(Child);

В приведенном выше примере очевидно, что дочерний компонент отображается только один раз из-за использования useCallback. Это эффективно предотвращает повторяющийся повторный рендеринг, вызванный повторяющимся вызовом метода incrementCountTwo каждый раз при загрузке родительского компонента, что приводит к повышению производительности и эффективности.

Заключение

Подводя итог, при сравнении useCallback и useMemo их основное различие заключается в возвращаемых результатах: useCallback предоставляет запоминаемую функцию обратного вызова, а useMemo предлагает запоминаемое значение.

Применяя эти методы, вы сможете оптимизировать производительность вашего приложения и обеспечить лучший пользовательский интерфейс.

Спасибо, что прочитали этот урок! Я надеюсь, что вы нашли его полезным и что он даст вам знания для создания высокопроизводительных приложений с использованием React.