如何在React Hooks的useEffect和setState中使用setTimeout()?

4

我想等待10秒钟,让我的API调用从后端获取类别列表数组并储存在钩子状态中。如果在10秒内没有获取到任何内容,我希望将错误钩子状态设置为true。

但问题是,即使最初已经获取了数组,错误状态也会被设置为true,并且10秒后,状态中的categoriesList数组会消失。

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

import { doGetAllCategories } from "../helper/adminapicall.js";

const ViewCategories = () => {
  let [values, setValues] = useState({
    categoriesList: "",
    error: false,
  });

  let { categoriesList, error } = values;

  const preloadCategories = () => {
    doGetAllCategories()
      .then((data) => {
        if (data.error) {
          return console.log("from preload call data - ", data.error);
        }
        setValues({ ...values, categoriesList: data.categories });
      })
      .catch((err) => {
        console.log("from preload - ", err);
      });
  };

  useEffect(() => {
    preloadCategories();

    let timerFunc = setTimeout(() => {
      if (!categoriesList && !error) {
        setValues({
          ...values,
          error: "Error fetching category list... try after some time !",
        });
      }
    }, 10000);

    return () => {
      clearTimeout(timerFunc);
    };
  }, []);



//...further code
2个回答

9
问题在于useEffect回调函数对categoriesList进行了闭包,因此您将始终在回调函数中看到初始的类别列表,并且您不会看到任何对其的更改。现在可以将categoriesList作为依赖项添加到useEffect钩子中,这样每当categoriesList更改时,钩子将被重新创建,从而您可以看到更改后的版本:
useEffect(/*...*/, [categoriesList]);

现在好消息是,通过更新钩子(hook),超时也将被取消,因此如果分类列表已设置,我们就不必创建新的超时:

  useEffect(() => {
    if(!categoriesList && !error) {
      let timerFunc = setTimeout(() => {
        setValues({
          ...values,
          error: "Error fetching category list... try after some time !",
        });
      }, 10000);

      return () => clearTimeout(timerFunc);
  }
}, [!categoriesList, !error]); // some minor optimization, changes to the list don't bother this hook

我建议你阅读 Dan Abramov 关于 useEffect 的这篇博客文章


天才!我已经四处寻找,这正是我所需要的。谢谢伙计,你救了我的一天! - Donald Shahini

-1
你的代码问题在于你期望在 useEffect 钩子内部改变组件状态。相反,你在 useEffect 内部创建了两个变量来跟踪是否超过了 10 秒的限制或者数据是否已经获取。与状态变量不同,你可以期望这些变量会发生变化,因为它们位于同一个 useEffect 内部。
export default function App() {
  const [data, setData] = React.useState(null);
  const [error, setError] = React.useState(null);
  React.useEffect(() => {
    let didCancel = false;
    let finished = false;
    async function fetchData() {
      const data = await subscribeAPI();
      if (!didCancel) {
        finished = true;
        setData(data);
      }
    }
    const id = setTimeout(() => {
      didCancel = true;
      if (!finished) {
        setError("Errorrrr");
      }
    }, 10000);

    fetchData();

    return () => {
      clearTimeout(id);
    };
  }, []);

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