ReactJS: как вызвать хук useEffect только один раз для получения данных API

Используя React, у меня есть следующий функциональный компонент, в котором я использую useEffect():

import React, { useEffect } from 'react';
import { connect } from 'react-redux';

const MessagingComponent = React.memo(({ loadMessages, messages, ...props }) => {

  useEffect(() => {
    loadMessages();
  });

  return <div {...props}>These are the messages</div>;
});

const mapDispatchToProps = dispatch => ({
  loadMessages: () => dispatch({ type: 'LOAD_MESSAGES' })
});

const mapStateToProps = state => {
  return {
    messages: state.chatt.messages.chatMessages
  };
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(MessagingComponent);

Как видите, у меня есть обратный вызов эффекта, который вызывает функцию loadMessages() в обратном вызове useEffect() в моем MessagingComponent:

  useEffect(() => {
    loadMessages();
  });

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

Как я могу этого избежать? Должен ли я просто поставить условие if в хук useEffect() и проверить свойство messages?

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
20
0
29 040
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Если я правильно понимаю, вы хотите имитировать поведение «при монтировании компонента» обычных реагирующих компонентов на основе классов через useEffect(), чтобы обратный вызов эффекта срабатывал только один раз при первом рендеринге.

Чтобы добиться такого поведения, вы можете передать пустой массив второму аргументу useEffect() следующим образом:

useEffect(() => {
  loadMessages();
}, []); /* <-- add this */

Второй аргумент массива позволяет указать, какие переменные prop вызывают обратный вызов useEffect() (если есть) при изменении их значений.

Передача пустого массива означает, что useEffect() не будет запускаться никакими изменениями входных prop переменных и, в свою очередь, будет вызываться только один раз, во время первого рендеринга.

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

Это вызывает предупреждение: React Hook useEffect has missing dependencies: loadMessages Either include them or remove the dependency array react-hooks/exhaustive-deps

talhatahir 21.06.2019 08:33

Что делать с предупреждением? Должны ли мы просто оставить это? Я использую редукционный преобразователь и делаю props.loadMessages(), и предупреждаю, что props — это отсутствующая зависимость. Что делать в таком случае? Спасибо

theprogrammer 16.11.2019 08:02

Чтобы избавиться от предупреждения, вы можете добавить свою функцию в массив зависимостей, например: useEffect(() => { loadMessages(); }, [loadMessages]);. Когда вы предоставляете массив зависимостей, ваш эффект будет работать только в том случае, если одна из зависимостей изменится. Таким образом, ваш эффект будет работать снова только в том случае, если ваша функция loadMessage изменится, чего, вероятно, не произойдет.

alextouzel 12.12.2019 20:12

@alextouzel добавление функции в массив deps вызовет бесконечный цикл

Gianluca Pietrobon 17.03.2021 23:08

@GianlucaPietrobon Если ссылка на функцию loadMessages не изменится, это не вызовет бесконечного цикла. По умолчанию mapDispatchToProps вызывается только один раз (при создании экземпляра вашего компонента), поэтому в этом случае ссылка на функцию должна остаться прежней, а хук useEffect будет запускаться только один раз, даже если среди его зависимостей есть loadMessages.

alextouzel 19.03.2021 01:44

useEffect() — это хук реагирования для функциональных компонентов, который охватывает функциональность хуков жизненного цикла компонентов класса. useEffect() объединяет componentDidMount(), componentDidUpdate() и componentWillUnmount() компонентов класса, что означает, что он будет выполняться при монтировании компонента, при изменении состояния компонента и при отключении компонента от DOM.

Проверьте ниже пример,

useEffect(() => {
   setTimeout(() => {
      alert("data saved");
    }, 1000);
});

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

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

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

useEffect(() => {
    setTimeout(() => {
      alert("data saved");
    }, 1000);
  }, [props.persons]);

Это выполнит код во время первоначального рендеринга и при изменении props.persons.

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

useEffect(() => {
    setTimeout(() => {
      alert("data saved");
    }, 1000);
  }, []);

Вы можете изменить свой хук useEffect(), как показано ниже, чтобы вызвать его только один раз для получения данных API,

useEffect(() => {
    loadMessages();
  }, []);

Другие вопросы по теме