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

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

Что такое крючки?

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

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

React предоставляет несколько встроенных хуков, таких как useState, useEffect, useCallback и useMemo и другие.

Некоторые из преимуществ, которые хуки предлагают по сравнению с традиционными компонентами класса, включают:

  • Простой код. Хуки устраняют необходимость в компонентах класса и обеспечивают более простой подход к управлению состоянием и побочными эффектами.
  • Повторное использование кода. Хуки позволяют повторно использовать логику с отслеживанием состояния в нескольких компонентах, уменьшая дублирование кода.
  • Улучшенная читабельность. Крючки упрощают понимание и анализ поведения отдельных компонентов.
  • Тестируемость. Хуки упрощают изолированное тестирование отдельных функций и компонентов.
  • Оптимизация производительности. Такие хуки, как useCallback и useMemo, повышают производительность за счет запоминания значений и предотвращения ненужного повторного рендеринга.

Крюк useEffect

Хук useEffect используется для выполнения побочных эффектов в функциональных компонентах. Побочные эффекты могут включать выборку данных, подписки или ручное изменение модели DOM. Хук принимает два аргумента: функцию и массив зависимостей.

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

import React, { useState, useEffect } from 'react';
import axios from 'axios';

const DataComponent = ({ id }) => {
  const [data, setData] = useState(null);

  useEffect(() => {
    // Fetch data from the API when the component first renders
    const fetchData = async () => {
      try {
        const response = await axios.get(`/api/data/${id}`);
        setData(response.data);
      } catch (error) {
        // Handle error if the API request fails
        console.error('Error fetching data:', error);
      }
    };

    fetchData();
  }, [id]);

  useEffect(() => {
    // Update the component when the 'id' prop changes
    const handlePropChange = () => {
      // Perform specific logical actions based on the 'id' prop
      if (id === 'some-value') {
        // Do something
      } else {
        // Do something else
      }
    };

    handlePropChange();

    // Cleanup function
    return () => {
      // Perform cleanup actions when the component unmounts or 'id' prop changes
      if (id === 'cleanup-value') {
        // Perform specific cleanup logic
        // ...
      } else {
        // Perform different cleanup logic
        // ...
      }
    };
  }, [id]);

  return (
    <div>
      {/* Render the fetched data */}
      {data ? (
        <div>{data}</div>
      ) : (
        <div>Loading data...</div>
      )}
    </div>
  );
};

В приведенном выше примере блока кода DataComponent извлекает данные из API при первом рендеринге с помощью хука useEffect. Функция эффекта определена в хуке useEffect и выполняет асинхронный вызов для получения данных. После успешного извлечения данных они сохраняются в состоянии компонента с помощью функции setData.

Хук useEffect также принимает массив зависимостей [id], который указывает, что эффект должен запускаться повторно при каждом изменении реквизита id. Это гарантирует, что компонент будет обновляться новыми данными при каждом изменении свойства id.

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

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

Распространенные варианты использования

  • Извлечение данных. Используйте useEffect для получения данных из API при монтировании компонента или при изменении определенных зависимостей.
  • Подписки. Управляйте подписками на внешние источники данных или генераторы событий с помощью useEffect.
  • Манипулирование DOM: если вам нужно напрямую взаимодействовать с DOM, useEffect — правильный хук.

Крюк useCallback

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

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

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

import React, { useCallback } from 'react';
import ChildComponent from './ChildComponent';

const ParentComponent = () => {
  const handleClick = useCallback((itemId) => {
    // Perform specific action based on the itemId
    if (itemId % 2 === 0) {
      console.log(`Item with ID ${itemId} is even.`);
    } else {
      console.log(`Item with ID ${itemId} is odd.`);
    }
  }, []);

  return (
    <div>
      {items.map((item) => (
        <ChildComponent
          key={item.id}
          item={item}
          onClick={handleClick}
        />
      ))}
    </div>
  );
};

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

Запоминая функцию handleClick с помощью useCallback, вы гарантируете, что она воссоздается только при изменении ее зависимостей (в этом случае нет никаких зависимостей, указанных пустым массивом зависимостей []). Эта оптимизация предотвращает ненужный повторный рендеринг дочерних компонентов и повышает производительность.

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

Распространенные варианты использования

  • Оптимизация дочерних компонентов. Используйте useCallback, чтобы предотвратить ненужный повторный рендеринг дочерних компонентов, которые полагаются на ссылки на функции для свойств.
  • Обработчики событий. При передаче обработчиков событий в качестве свойств или действий, таких как действия onClick, useCallback гарантирует, что ссылка остается стабильной, предотвращая ненужные повторные рендеринг.

Крюк useMemo

Хук useMemo похож на useCallback, но вместо запоминания функций он запоминает значения.

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

import React, { useMemo } from 'react';

const ItemList = ({ data, options }) => {
  const expensiveCalculationResult = useMemo(() => {
    // Perform complex and computationally expensive calculation
    let result = [];

    for (let i = 0; i < data.length; i++) {
      // Perform some intensive operations on each item
      // ...
      // Example: Concatenate the item name with the options value
      const modifiedItem = {
        ...data[i],
        nameWithOptions: `${data[i].name} - ${options}`,
      };

      result.push(modifiedItem);
    }

    return result; // The calculated result
  }, [data, options]);

  return (
    <div>
      {expensiveCalculationResult.map((item) => (
        <div key={item.id}>{item.nameWithOptions}</div>
      ))}
    </div>
  );
};

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

При запоминании результата с помощью useMemo ресурсоемкий расчет будет выполняться снова, только если изменятся зависимости data или options. В противном случае будет возвращен ранее запомненный результат, что позволит избежать лишних вычислений и повысить производительность компонента.

Распространенные варианты использования

  • Дорогие вычисления. Используйте useMemo, чтобы избежать избыточных вычислений, которые требуют больших вычислительных ресурсов.
  • Кэширование значений. Если значение не меняется часто и его вычисление требует больших ресурсов, useMemo может кэшировать результат до тех пор, пока не изменятся зависимости.

Различия между useEffect, useCallback и useMemo

Хотя useEffect, useCallback и useMemo — это хуки, используемые в React, они служат разным целям:

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

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

Ключевые выводы

Чтобы максимально использовать эти хуки, рассмотрите следующие рекомендации:

  1. Используйте useEffect для получения данных, подписок и других побочных эффектов.
  2. Используйте useCallback при передаче обратных вызовов дочерним компонентам, чтобы предотвратить ненужный повторный рендеринг.
  3. Используйте useMemo для оптимизации дорогостоящих вычислений или сложных операций.
  4. Тщательно выбирайте зависимости для каждого хука, чтобы убедиться, что они точны и актуальны.
  5. Избегайте ненужных повторных рендеров, используя соответствующий хук для каждого сценария.

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