ReactJS:如何仅调用一次useEffect钩子以获取API数据

22

使用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);

正如您所看到的,我有一个效果回调函数,在我的 MessagingComponent 中的 useEffect() 回调中调用了 loadMessages() 函数:

  useEffect(() => {
    loadMessages();
  });
调用loadMessages()会加载消息,导致组件重新渲染。这种行为是预期的,但问题在于重新渲染会导致useEffect()钩子再次触发,从而再次调用loadMessage()。这反过来会从后端加载消息并导致组件再次渲染,循环重复。
如何避免这种情况?我是否应该在useEffect()钩子中简单地放置一个if条件,并检查消息属性?
2个回答

44

如果我理解正确的话,您想通过useEffect()来模拟常规基于类的React组件的"on component mounted"行为,以便效果回调只在第一次呈现时触发。

为了实现这种行为,您可以将一个空数组传递给useEffect()的第二个参数,像这样:

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

第二个数组参数允许您指定哪些prop变量在其值发生更改时触发useEffect()回调(如果有的话)。

通过传递一个空数组,这意味着useEffect()不会因为任何输入prop变量的更改而被触发,并且只会在第一次呈现期间调用一次。

有关此第二个参数的更多信息,请参见此文档此文档


25
这会造成一个警告: React Hook useEffect 缺少依赖项:loadMessages,请在依赖项数组中包含它们,或者删除依赖项数组 react-hooks/exhaustive-deps - talhatahir
警告该怎么办?我们应该就这样放着吗?我正在使用redux thunk并执行props.loadMessages(),警告是props是一个缺失的依赖项。在这种情况下该怎么办?谢谢 - theprogrammer
为了消除警告,您可以将函数添加到依赖项数组中,像这样:useEffect(() => { loadMessages(); }, [loadMessages]);。当您提供一组依赖项时,只有在其中一个依赖项更改时,才会运行您的效果。因此,如果您的 loadMessage 函数不会更改,那么您的效果也不会再次运行。 - alextouzel
1
@alextouzel 在 deps 数组内添加该函数将导致无限循环。 - Gianluca Pietrobon
如果 loadMessages 函数的引用不改变,它将不会导致无限循环。默认情况下,mapDispatchToProps 只会在组件实例创建时调用一次,因此在这种情况下,函数引用应该保持不变,即使它在其依赖项中有 loadMessages,useEffect 钩子也只会运行一次。 - alextouzel

12

useEffect()是React函数组件中的钩子,涵盖了类组件的生命周期钩子的功能。 useEffect()结合了类组件的componentDidMount(),componentDidUpdate()componentWillUnmount(),这意味着它会在组件挂载时执行,在组件状态改变时执行以及在组件从DOM中卸载时执行。

请查看下面的例子,

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

当组件被挂载/初始化渲染时,当组件状态改变以及当组件从DOM中卸载时,以上代码/警报将被执行。

为了使上述代码运行次数更少,我们可以将一个值数组作为第二个参数传递进去,作为该效果的依赖项。如果其中一个依赖项发生变化,则该效果将再次运行。

考虑我们有一个persons数组传递到函数组件的props中,我们希望仅在persons数组发生更改时执行警报。

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

这将在初始渲染时和props.persons更改时执行代码。

因此,只需在初始渲染/组件安装期间执行代码,您可以传递一个空数组,表示在应该执行效果的依赖项未指定时。 因此,在初始渲染时和组件卸载时运行。

useEffect(() => {
    setTimeout(() => {
      alert("data saved");
    }, 1000);
  }, []);
您可以修改您的useEffect()钩子,如下所示,仅调用一次以获取API数据。
useEffect(() => {
    loadMessages();
  }, []);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接