reactjs - 在 useEffect 内模拟 React 设置 2 个状态
问题描述
我有一个组件,用于显示这样的数据条目列表(简化):
// resource is the rest endpoint,
// items is the parents components
// state that holds the data entries and setItems is the corresponding setter
export default function LoadedList({resource,items, setItems,CustomFormat}){
const [loadingOrError,setLoadingOrError] =useState(false)
useEffect(()=>{
axios.get(baseURL+resource)
.then((e)=>{
setItems(e.data)
setLoadingOrError(false)
})
.catch((e)=>{
setItems([{text:"Error"}])
setLoadingOrError(true)
})
setItems([{text:"Loading...-"}])
setLoadingOrError(true)
},[])
return(
<div className="list">
{
items.map((item)=>
loadingOrError?
<DefaultFormat item={item} />
:
<CustomFormat item={item}/>
)
}
</div>
)
}
基本思想是,当组件加载项目或失败时,应使用默认格式显示相应的消息。成功加载项目后,应使用父项的格式来格式化条目。问题是,我发现 setItems 和 setLoading 没有同时更改。它似乎工作的方式是它首先 setItems 然后重新呈现所有条目,然后才将 loadingOrError 更改为 true。那么有没有办法同时设置这两个?或者只是不重新渲染其间的一切?
解决方案
与其尝试同时更新两者,不如尝试分别跟踪加载和错误状态,然后执行以下操作:
// resource is the rest endpoint,
// items is the parents components
// state that holds the data entries and setItems is the corresponding setter
export default function LoadedList({resource, items, setItems, CustomFormat}){
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
useEffect(()=>{
setLoading(true);
axios.get(baseURL+resource)
.then((e)=>
setItems(e.data)
)
.catch((e)=>
setError("Error")
)
.finally(() => setLoading(false));
},[])
if(loading) {
return "Loading ...";
}
if(error) {
return error;
}
return(
<div className="list">
{items.map((item, index) => <CustomFormat key={index} item={item}/>)}
</div>
)
}
这应该会显示Loading...
,直到所有项目都加载完毕。
如果您坚持要保持一切原样,并且只是实现您最初要求的同时更新两者,您可能需要定义一个函数来执行上一级的 API 调用,以及加载状态,错误状态和数据状态处理,将所有这些状态放在同一个状态钩子下,然后将 API 函数传递给孩子的 useEffect 使用。
const [dataState, setDataState] = useState({
data: null,
loading: false,
error: ""
});
...
setDataState({data: data, loading: false});
除此之外,我推荐两件事:
- 您应该在请求完成时和设置状态之前检查组件是否仍然挂载。否则你会得到一个错误。这很容易通过一个额外的变量来跟踪挂载状态。
- 为处理请求创建一个自定义钩子可能是有益的,因为这可能是您经常要做的事情,并且在每种情况下看起来都非常相似。我发现这篇文章中的分步指南非常清楚。
取自那篇文章:
useFetch 自定义钩子
import { useState, useEffect } from 'react';
const useFetch = (url = '', options = null) => {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
let isMounted = true;
setLoading(true);
fetch(url, options)
.then(res => res.json())
.then(data => {
if (isMounted) {
setData(data);
setError(null);
}
})
.catch(error => {
if (isMounted) {
setError(error);
setData(null);
}
})
.finally(() => isMounted && setLoading(false));
return () => (isMounted = false);
}, [url, options]);
return { loading, error, data };
};
export default useFetch;
推荐阅读
- php - 如何以相同的日期时间从 phpmyadmin 获取值?
- python - 在另一列中搜索值并获取相应的值 python
- amazon-web-services - 在 S3 中自动创建文件夹
- javascript - 我可以使用 npm 链接来附加 React 组件吗?
- r - 从R中的列中查找下一个大于日期的日期
- sql - SQL - 为 B 列中缺少的每个不同元素的 A 列中的每个不同元素插入值 = 0 的行
- python - 从pygame中的程序退出
- node.js - 如何使用节点js重定向到url
- javascript - 如何捕获迭代的数组的索引(Javascript)
- c - 无法添加到链接列表的头部