首页 > 解决方案 > 如何将 setTimeout() 与 React Hooks useEffect 和 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

标签: javascriptreactjsreact-hookssettimeoutuse-effect

解决方案


问题是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

我建议您阅读Dan Abramov 的这篇关于 useEffect 的博文


推荐阅读