如何将 setTimeout() 与 React Hooks useEffect 和 setState 一起使用?

How to use setTimeout() along with React Hooks useEffect and setState?

我想等待 10 秒,以便我的 API 调用从后端获取类别列表数组并存储在挂钩状态。如果 10 秒内未获取任何内容,我想将错误挂钩状态设置为 true。

但问题是即使在最初获取数组后,错误状态设置为 true 并且状态中的 categoriesList 数组在 10 秒后空白。

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

问题在于 useEffect 回调是 categoriesList 上的闭包,因此您始终会在回调中看到初始类别列表,并且看不到任何更改.现在可以添加 categoriesList 作为 useEffect 挂钩的依赖项,这样挂钩将在每次 categoriesList 更改时重新创建,因此您可以看到更改后的版本:

useEffect(/*...*/, [categoriesList]);

现在好事是,通过更新挂钩超时也被取消,所以如果设置了类别列表,我们就不必创建新的超时:

  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

我推荐你阅读this blog post on useEffect by Dan Abramov

您的代码存在问题,您希望在 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);
    };
  }, []);