如何将setTimeout()与React挂钩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

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

我建议你阅读丹·阿布拉莫夫关于useEffect的博客文章。

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

最新更新