React中useEffect执行顺序问题:API数据未加载完成无法复用
React中useEffect依赖数据未就绪的解决方案
你的核心问题是第二个useEffect的依赖数组为空,仅在组件挂载时执行一次,此时第一个useEffect的API请求还未完成(耗时8秒),blueCardData还是初始值,isLoading也未更新,导致无法获取有效数据。此外代码中还有两处细节问题:
blueCardData初始设为数组[],但API返回的是对象(你后续访问blueCardData.today_revenue等属性),类型不匹配可能引发报错- 图表数据处理逻辑错误:
arr3始终为空,arr4同时存入时间和数值,导致图表的labels和data完全不符合预期
方案1:修正useEffect依赖,确保数据就绪后执行
将第二个useEffect的依赖数组改为[blueCardData, isLoading],这样每当blueCardData或isLoading更新时,都会重新执行该逻辑,保证数据就绪后再处理图表数据。同时修正数组处理逻辑和初始状态:
import React, { useState, useEffect } from "react"; import { Col, Container, Row } from "react-bootstrap"; import Bluecard from "../../components/bluecard/Bluecard"; import Linechart from "../../charts/linechart/Linechart"; import Solar_1 from "../../assets/images/Solar_1.png"; import Solar_2 from "../../assets/images/Solar_2.png"; import Solar_3 from "../../assets/images/Solar_3.png"; import Solar_4 from "../../assets/images/Solar_4.png"; import Solar_5 from "../../assets/images/Solar_5.png"; import Solar_6 from "../../assets/images/Solar_6.png"; import Solar_7 from "../../assets/images/Solar_7.png"; import Solar_8 from "../../assets/images/Solar_8.png"; function Solarcopy() { const [isLoading, setIsLoading] = useState(true); // 初始设为null,避免对象属性访问报错 const [blueCardData, setBlueCardData] = useState(null); const [lineData, setLineData] = useState({ labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun"], datasets: [ { label: "Users Gained", data: [10, 20, 30, 10, 15, 30], backgroundColor: [ "rgba(75,192,192,1)", "#ecf0f1", "#50AF95", "#f3ba2f", "#2a71d0", ], borderColor: "black", borderWidth: 2, fill: true, }, ], }); // 获取API数据,增加错误处理 useEffect(() => { fetch("http://192.168.18.21:8000/performance/solar-overview/") .then((response) => { if (!response.ok) throw new Error("请求失败"); return response.json(); }) .then((dataValues) => { setBlueCardData(dataValues); setIsLoading(false); }) .catch((err) => { console.error(err); setIsLoading(false); // 即使失败也停止加载状态 }); }, []); // 处理图表数据,依赖blueCardData和isLoading useEffect(() => { // 数据未就绪时直接返回 if (isLoading || !blueCardData?.pr_hourly) return; const labels = blueCardData.pr_hourly.time; const data = blueCardData.pr_hourly.pr_hourly; setLineData({ labels, datasets: [ { label: "Values", data, backgroundColor: [ "rgba(75,192,192,1)", "#ecf0f1", "#50AF95", "#f3ba2f", "#2a71d0", ], borderColor: "black", borderWidth: 2, fill: true, }, ], }); }, [blueCardData, isLoading]); if (isLoading) { return <p>Loading Data</p>; } // 数据加载完成但blueCardData为空时的兜底 if (!blueCardData) { return <p>数据加载失败</p>; } return ( <> <section> <Container fluid> <Row> <Col lg={6}> <Row> <Col xl={6} className="mt-3"> <Bluecard heading={blueCardData.today_revenue} spanText="PKR" text="Today's Revenue" image={Solar_1} /> </Col> <Col xl={6} className="mt-3"> <Bluecard heading={blueCardData.cuf} spanText="%" text="CUF" image={Solar_2} /> </Col> <Col xl={6} className="mt-3"> <Bluecard heading={blueCardData.total_yield} spanText="KWH" text="Today's Yield" image={Solar_3} /> </Col> <Col xl={6} className="mt-3"> <Bluecard heading={blueCardData.cuf} spanText="%" text="CUF" image={Solar_4} /> </Col> <Col xl={6} className="mt-3"> <Bluecard heading={blueCardData.system_size} spanText="kW" text="System Size" image={Solar_5} /> </Col> <Col xl={6} className="mt-3"> <Bluecard heading={blueCardData.inverters} spanText="" text="Number Of Inverters" image={Solar_6} /> </Col> <Col xl={6} className="mt-3"> <Bluecard heading={blueCardData.performance_ratio} spanText="%" text="Performance Ratio" image={Solar_7} /> </Col> <Col xl={6} className="mt-3"> <Bluecard heading={blueCardData.tc_performance_ratio} spanText="%" text="TCPR" image={Solar_8} /> </Col> </Row> </Col> </Row> </Container> </section> <section className="mt-lg-5 mt-5"> <Container fluid> <Row> <Col lg={6}> <Linechart chartData={lineData} /> </Col> </Row> </Container> </section> </> ); } export default Solarcopy;
方案2:合并useEffect,减少不必要的渲染
如果图表数据不需要单独监听其他状态,可以直接在API请求完成后处理图表数据,这样避免额外的useEffect执行:
// 替换原来的两个useEffect为以下代码 useEffect(() => { fetch("http://192.168.18.21:8000/performance/solar-overview/") .then((response) => { if (!response.ok) throw new Error("请求失败"); return response.json(); }) .then((dataValues) => { setBlueCardData(dataValues); // 直接处理图表数据 if (dataValues?.pr_hourly) { const labels = dataValues.pr_hourly.time; const data = dataValues.pr_hourly.pr_hourly; setLineData({ labels, datasets: [ { label: "Values", data, backgroundColor: [ "rgba(75,192,192,1)", "#ecf0f1", "#50AF95", "#f3ba2f", "#2a71d0", ], borderColor: "black", borderWidth: 2, fill: true, }, ], }); } setIsLoading(false); }) .catch((err) => { console.error(err); setIsLoading(false); }); }, []);
关键优化点总结
- 修正依赖数组:确保依赖项包含需要监听的状态,保证数据更新时触发逻辑
- 初始状态匹配:
blueCardData初始设为null或{},避免访问对象属性时的类型错误 - 增加错误处理:API请求失败时捕获错误,避免加载状态一直挂起
- 简化数组处理:直接提取
pr_hourly中的time和pr_hourly数组,无需额外map和push操作
内容的提问来源于stack exchange,提问作者Talha
相关产品推荐
相关产品推荐

