使用React useEffect设置加载状态失败问题排查
问题原因
你的isLoading状态提前变为false,核心问题出在第二个useEffect的触发逻辑上:
- 组件初始化时,
problems的初始值是空数组[],error为undefined - 第一个
useEffect调用TierService.getProblems后,不管API请求是否完成,第二个useEffect会因为problems和error的初始状态立即触发,直接执行setIsLoading(false),导致加载动画提前消失
解决方案
方案1:在API回调中手动控制加载状态
假设TierService.getProblems通过回调返回结果/错误,修改第一个useEffect,在请求完成(成功或失败)后再关闭加载状态:
const ProblemTable: React.FC<IProblemTableProps> = (props) => { const [problems, setProblems] = useState<IProblem[]>([]); const [isLoading, setIsLoading] = useState<boolean>(true); const [error, setError] = useState<Error>(); useEffect(() => { setIsLoading(true); TierService.getProblems( props.tier, (data) => { setProblems(data); setIsLoading(false); }, (err) => { setError(err); setIsLoading(false); } ); }, [props.tier]); if (isLoading) { return <Loading message="Loading problems..." />; } if (error) { return <ErrorMessage title="Failed fetching problems" />; } return <>...</>; };
方案2:改用Promise+async/await(推荐)
如果能修改TierService.getProblems让它返回Promise,代码会更简洁易维护:
// 先调整TierService.getProblems的实现,让它返回Promise // 示例: // class TierService { // static getProblems(tier: string): Promise<IProblem[]> { // return fetch(`/api/problems?tier=${tier}`).then(res => res.json()); // } // } const ProblemTable: React.FC<IProblemTableProps> = (props) => { const [problems, setProblems] = useState<IProblem[]>([]); const [isLoading, setIsLoading] = useState<boolean>(true); const [error, setError] = useState<Error>(); useEffect(() => { const fetchProblems = async () => { setIsLoading(true); try { const data = await TierService.getProblems(props.tier); setProblems(data); } catch (err) { setError(err as Error); } finally { // 不管成功失败,都关闭加载状态 setIsLoading(false); } }; fetchProblems(); }, [props.tier]); if (isLoading) { return <Loading message="Loading problems..." />; } if (error) { return <ErrorMessage title="Failed fetching problems" />; } return <>...</>; };
关键优化点
- 移除原来的第二个
useEffect,它无法区分“初始空数组”和“请求返回的空结果”,不能准确判断请求状态 - 将
props.tier加入依赖数组,确保tier变化时重新发起请求,符合React Hooks规范 - 用
finally块(或回调)确保加载状态一定会被关闭,避免出现加载动画一直显示的情况
内容的提问来源于stack exchange,提问作者mikovacs
相关产品推荐
相关产品推荐

