简体   繁体   English

带有 useEffect 的无限循环 - ReactJS

[英]Infinite Loop with useEffect - ReactJS

I have a problem when using the useEffect hook, it is generating an infinite loop.我在使用 useEffect 钩子时遇到问题,它生成了一个无限循环。

I have a list that is loaded as soon as the page is assembled and should also be updated when a new record is found in "developers" state.我有一个在页面组装后立即加载的列表,并且在“开发人员”状态下发现新记录时也应该更新。

See the code:看代码:

  const [developers, setDevelopers] = useState<DevelopersData[]>([]);

  const getDevelopers = async () => {
    await api.get('/developers').then(response => {
      setDevelopers(response.data);
    });
  };
  
  // This way, the loop does not happen
  useEffect(() => {
    getDevelopers();
  }, []);

  // This way, infinte loop
  useEffect(() => {
    getDevelopers();
  }, [developers]);

  console.log(developers)

If I remove the developer dependency on the second parameter of useEffect, the loop does not happen, however, the list is not updated when a new record is found.如果我删除开发人员对 useEffect 的第二个参数的依赖,则不会发生循环,但是,在找到新记录时不会更新列表。 If I insert "developers" in the second parameter of useEffect, the list is updated automatically, however, it goes into an infinite loop.如果我在 useEffect 的第二个参数中插入“developers”,列表会自动更新,但是,它会进入无限循环。

What am I doing wrong?我究竟做错了什么?

complete code (with component): https://gist.github.com/fredarend/c571d2b2fd88c734997a757bac6ab766完整代码(带组件): https : //gist.github.com/fredarend/c571d2b2fd88c734997a757bac6ab766

Print:打印: 在此处输入图片说明

The dependencies for useEffect use reference equality, not deep equality. useEffect的依赖使用引用相等,而不是深度相等。 (If you need deep equality comparison for some reason, take a look at use-deep-compare-effect .) (如果出于某种原因需要进行深度相等比较,请查看use-deep-compare-effect 。)

The API call always returns a new array object, so its reference/identity is not the same as it was earlier, triggering useEffect to fire the effect again, etc. API 调用总是返回一个新的数组对象,因此它的引用/标识与之前的不一样,触发useEffect再次触发效果等。

Given that nothing else ever calls setDevelopers , ie there's no way for developers to change unless it was from the API call triggered by the effect, there's really no actual need to have developers as a dependency to useEffect ;鉴于没有其他任何东西调用setDevelopers ,即developers无法更改,除非它来自效果触发的 API 调用,因此实际上没有实际需要让developers作为useEffect的依赖useEffect you can just have an empty array as deps: useEffect(() => ..., []) .你可以有一个空数组作为 deps: useEffect(() => ..., []) The effect will only be called exactly once.该效果只会被调用一次。

EDIT: Following the comment clarification,编辑:在评论澄清之后,

I register a developer in the form on the left [...] I would like the list to be updated as soon as a new dev is registered.我在左侧的表格中注册了一名开发人员 [...] 我希望在注册新开发人员后立即更新列表。

This is one way to do things:这是做事的一种方式:

The idea here is that developers is only ever automatically loaded on component mount.这里的想法是developers只会在组件安装时自动加载。 When the user adds a new developer via the AddDeveloperForm , we opportunistically update the local developers state while we're posting the new developer to the backend.当用户通过AddDeveloperForm添加新开发developers时,我们会在将新开发人员发布到后端的同时机会性地更新本地developers状态。 Whether or not posting fails, we reload the list from the backend to ensure we have the freshest real state.无论发布是否失败,我们都会从后端重新加载列表以确保我们拥有最新的真实状态。

const DevList: React.FC = () => {
  const [developers, setDevelopers] = useState<DevelopersData[]>([]);

  const getDevelopers = useCallback(async () => {
    await api.get("/developers").then((response) => {
      setDevelopers(response.data);
    });
  }, [setDevelopers]);

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

  const onAddDeveloper = useCallback(
    async (newDeveloper) => {
      const newDevelopers = developers.concat([newDeveloper]);
      setDevelopers(newDevelopers);
      try {
        await postNewDeveloperToAPI(newDeveloper); // TODO: Implement me
      } catch (e) {
        alert("Oops, failed posting developer information...");
      }
      getDevelopers();
    },
    [developers],
  );

  return (
    <>
      <AddDeveloperForm onAddDeveloper={onAddDeveloper} />
      <DeveloperList developers={developers} />
    </>
  );
};

The problem is that your getDevelopers function, calls your setDevelopers function, which updates your developers variable.问题是您的getDevelopers函数调用了setDevelopers函数,该函数更新了您的developers变量。 When your developers variable is updated, it triggers the useEffect function当您的developers变量更新时,它会触发useEffect函数

  useEffect(() => {
    getDevelopers();
  }, [developers]);

because developers is one of the dependencies passed to it and the process starts over.因为developers是传递给它的依赖项之一,并且该过程重新开始。

Every time a variable within the array, which is passed as the second argument to useEffect, gets updated, the useEffect function gets triggered每次更新数组中作为 useEffect 的第二个参数的变量时,都会触发useEffect函数

Use an empty array [] in the second parameter of the useEffect.在 useEffect 的第二个参数中使用空数组[] This causes the code inside to run only on mount of the parent component.这会导致内部代码仅在父组件的安装上运行。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM