如何为React统计页面添加全局Spinner Loader?
如何为Dashboard页面添加全局Spinner Loader
我有一个包含多个界面的项目,其中有一个展示各类统计数据的界面,该界面中每个图表的数据都通过对应API获取。现在需要为这个页面添加全局Spinner Loader,请问该如何实现?以下是负责展示所有统计内容的Dashboard组件代码:
import { Card, Col, Row, Spin } from 'antd'; import { FunctionComponent, useContext, useEffect, useState } from 'react'; import '../../../styles/dashboard/index.scss'; import { AuthContext, IAuthContext } from '../../../contexts/auth-context'; import colors from '../../../constants/colors'; import calendar1 from '../../../assets/icons/calendar1-icon.svg'; import waving from '../../../assets/icons/waving-hand-icon.svg'; import { FormattedMessage } from 'react-intl'; import BarCharts from './charts/barCharts'; import PieCharts from './charts/pieCharts'; import { useQuery } from "react-query"; import statisticCharts from '../../../api/nuclearMedicineApi/services/Statistic'; import { ChartEntityType1, ChartEntityType2, ChartEntityType3, ChartEntityTypeAnalysis } from './types'; import schedule from "../../../api/nuclearMedicineApi/services/Schedule"; import { MachineCategory } from '../../../constants/enums'; interface DashboardProps { } const Dashboard: FunctionComponent<DashboardProps> = () => { const current = new Date(); const date = `${current.getFullYear()}/${current.getMonth() + 1 }/${current.getDate()}`; const auth = useContext<IAuthContext>(AuthContext); const today = new Date(); var year = today.getFullYear(); const [xytotalTopography, setxytotalTopography] = useState<any[]>([]); const [xtopography, setXTopography] = useState<any[]>([]); const [xyAge, setxyAge] = useState<any[]>([]); const [xyCitytotalTopography, setxyCitytotalTopography] = useState<any[]>([]); const [xyTopographyPercent, setxyTopographyPercent] = useState<any[]>([]); const [analysisCountPatient, setAnalysisCountPatient] = useState<any[]>([]); const [ctCountPatient, setCtCountPatient] = useState<any[]>([]); const [mriCountPatient, setMriCountPatient] = useState<any[]>([]); const [gammaCountPatient, setGammaCountPatient] = useState<any[]>([]); const [xrayCountPatient, setXrayCountPatient] = useState<any[]>([]); const [cesiumCountPatient, setCesiumCountPatient] = useState<any[]>([]); const [cobaltCountPatient, setCobaltCountPatient] = useState<any[]>([]); const [iodinCountPatient, setIodinCountPatient] = useState<any[]>([]); const [linacCountPatient, setLinacCountPatient] = useState<any[]>([]); const chartsQueryTopographyData = useQuery(['chartsQueryTopographyData'], () => statisticCharts.GetTopographyStatistic({ FilterType: 1, StartDate: `${year}-01-01`, EndDate: `${year}-12-31` }), ).data; const chartsQueryCityData = useQuery(['chartsQueryCityData'], () => statisticCharts.GetTopographyStatistic({ FilterType: 2, StartDate: `${year}-01-01`, EndDate: `${year}-12-31` }), ).data; const chartsQueryAgeData = useQuery(['chartsQueryAgeData'], () => statisticCharts.GetTopographyStatistic({ FilterType: 3, StartDate: `${year}-01-01`, EndDate: `${year}-12-31` }), ).data; // count const schedulePatientCount = useQuery('schedulePatientCount', () => schedule.SchedulePatientCountGetAllLite({ machineCategory: MachineCategory.Analysiss, }), ).data; const schedulePatientCountiodin = useQuery('schedulePatientCountiodin', () => schedule.SchedulePatientCountGetAllLite({ machineCategory: MachineCategory.RadioTherapyByIodine, }), ).data; const schedulePatientCountCt = useQuery('schedulePatientCountCt', () => schedule.SchedulePatientCountGetAllLite({ machineCategory: MachineCategory.RadialImageByCT, }), ).data; const schedulePatientCountGamma = useQuery('schedulePatientCountGamma', () => schedule.SchedulePatientCountGetAllLite({ machineCategory: MachineCategory.RadialImageByGamma, }), ).data; const schedulePatientCountMri = useQuery('schedulePatientCountMri', () => schedule.SchedulePatientCountGetAllLite({ machineCategory: MachineCategory.RadialImageByMRI, }), ).data; const schedulePatientCountXray = useQuery('schedulePatientCountXray', () => schedule.SchedulePatientCountGetAllLite({ machineCategory: MachineCategory.RadialImageByXRay, }), ).data; const schedulePatientCountcesium = useQuery('schedulePatientCountcesium', () => schedule.SchedulePatientCountGetAllLite({ machineCategory: MachineCategory.RadioTherapyByCesium, }), ).data; const schedulePatientCountCobalt = useQuery('schedulePatientCountCobalt', () => schedule.SchedulePatientCountGetAllLite({ machineCategory: MachineCategory.RadioTherapyByCobalt, }), ).data; const schedulePatientCountLinac = useQuery('schedulePatientCountLinac', () => schedule.SchedulePatientCountGetAllLite({ machineCategory: MachineCategory.RadioTherapyByLinac, }), ).data; // *********** this function return array of arrays const cleanData1 = (data: ChartEntityType1[]) => data?.map((xy: ChartEntityType1) => { if (xy?.x !== undefined && xy?.yMale !== undefined && xy?.yFemale !== undefined) return [xy?.x, xy?.yMale, xy?.yFemale] }); const cleanData2 = (data: ChartEntityType2[]) => data?.map((xy: ChartEntityType2) => { if (xy?.x !== undefined && xy?.yTotal !== undefined) return [xy?.x, xy?.yTotal] }); const cleanData3 = (data: ChartEntityType3[]) => data?.map((xy: ChartEntityType3) => { if (xy?.x !== undefined && xy?.yPercent !== undefined) return [xy?.x, xy?.yPercent] }); const cleanData4 = (data: ChartEntityTypeAnalysis[]) => data?.map((xy: ChartEntityTypeAnalysis) => { if (xy !== undefined) if (xy?.count !== undefined && xy?.machine?.label !== undefined) return [xy?.machine?.label, xy?.count] }); useEffect(() => { // x = topography, y= male, y1= female // *************** this return array of arrays const mappedData1 = cleanData1(chartsQueryTopographyData); const mappedData2 = cleanData2(chartsQueryTopographyData); const mappedData3 = cleanData2(chartsQueryAgeData); const mappedData4 = cleanData3(chartsQueryCityData); const mappedData5 = cleanData3(chartsQueryTopographyData); const mappedData6 = cleanData4(schedulePatientCount); const mappedData7 = cleanData4(schedulePatientCountCt); const mappedData8 = cleanData4(schedulePatientCountGamma); const mappedData9 = cleanData4(schedulePatientCountMri); const mappedData10 = cleanData4(schedulePatientCountXray); const mappedData11 = cleanData4(schedulePatientCountCobalt); const mappedData12 = cleanData4(schedulePatientCountcesium); const mappedData13 = cleanData4(schedulePatientCountiodin); const mappedData14 = cleanData4(schedulePatientCountLinac); if (mappedData1) { setxytotalTopography([['topography', 'ذكر', 'أنثى'], ...mappedData1]) } if (mappedData2) { setXTopography([['topography', 'المرضى المصابين'], ...mappedData2]) } if (mappedData3) { setxyAge([['age', 'المرضى المصابين'], ...mappedData3]) } if (mappedData4) { setxyCitytotalTopography([['', ''], ...mappedData4]) } if (mappedData5) { setxyTopographyPercent([['', ''], ...mappedData5]) } if (mappedData6) { setAnalysisCountPatient([['', ''], ...mappedData6]) } if (mappedData7) { setCtCountPatient([['', ''], ...mappedData7]) } if (mappedData8) { setGammaCountPatient([['', ''], ...mappedData8]) } if (mappedData9) { setMriCountPatient([['', ''], ...mappedData9]) } if (mappedData10) { setXrayCountPatient([['', ''], ...mappedData10]) } if (mappedData11) { setCobaltCountPatient([['', ''], ...mappedData11]) } if (mappedData12) { setCesiumCountPatient([['', ''], ...mappedData12]) } if (mappedData13) { setIodinCountPatient([['', ''], ...mappedData13]) } if (mappedData14) { setLinacCountPatient([['', ''], ...mappedData14]) } }, [chartsQueryTopographyData, chartsQueryCityData, chartsQueryAgeData, schedulePatientCount, schedulePatientCountCt, schedulePatientCountGamma, schedulePatientCountMri, schedulePatientCountXray, schedulePatientCountCobalt, schedulePatientCountcesium, schedulePatientCountiodin, schedulePatientCountLinac]); return ( <> <div className='dashdoard-donutData'> {/* welcome */} <Row> <Card className='card-welcome'> <Row className='center'> <Col lg={20}> <Row> <Col> <FormattedMessage id='welcome' /> {' '} {auth.userData?.fullName} ! </Col> <Col className='mr-2 ml-2'> <img src={waving} alt='waving' /> </Col> <Col> <small> <FormattedMessage id='follow' /> </small> </Col> </Row> </Col> <Col lg={4}> <Row style={{ color: colors.primaryColor, }} justify='end' > <Col> <img src={calendar1} alt='calendar1' /> </Col> <Col> {' '} {'\u00a0\u00a0'}{' '} <FormattedMessage id='today' /> {date} </Col> </Row> </Col> </Row> </Card> </Row> {/* Close Date Appointment*/} {/* <ClosestFreeAppointment /> */} {/* Bar Charts */} {/* <BarCharts topographyData={chartsQueryTopographyData?.data} ageData={chartsQueryAgeData?.data} year={year} /> */} <BarCharts xytotalTopography={xytotalTopography} xtopography={xtopography} xyAge={xyAge} /> {/* Pie Charts */} <PieCharts xyCitytotalTopography={xyCitytotalTopography} xyTopographyPercent={xyTopographyPercent} analysisCountPatient={analysisCountPatient} ctCountPatient={ctCountPatient} mriCountPatient={mriCountPatient} gammaCountPatient={gammaCountPatient} xrayCountPatient={xrayCountPatient} cesiumCountPatient={cesiumCountPatient} cobaltCountPatient={cobaltCountPatient} iodinCountPatient={iodinCountPatient} linacCountPatient={linacCountPatient} year={year} /> </div> </> ); }; export default Dashboard;
实现方案
你的项目使用了React Query管理API请求,直接利用它提供的加载状态就能实现全局Spinner,无需额外维护复杂状态,具体步骤如下:
1. 保留完整的useQuery返回对象
之前你只提取了.data,现在需要保留每个useQuery的完整对象,这样才能获取isLoading状态:
// 替换原只取.data的写法,保留完整query对象 const chartsQueryTopographyDataQuery = useQuery(['chartsQueryTopographyData'], () => statisticCharts.GetTopographyStatistic({ FilterType: 1, StartDate: `${year}-01-01`, EndDate: `${year}-12-31` }), ); const chartsQueryCityDataQuery = useQuery(['chartsQueryCityData'], () => statisticCharts.GetTopographyStatistic({ FilterType: 2, StartDate: `${year}-01-01`, EndDate: `${year}-12-31` }), ); const chartsQueryAgeDataQuery = useQuery(['chartsQueryAgeData'], () => statisticCharts.GetTopographyStatistic({ FilterType: 3, StartDate: `${year}-01-01`, EndDate: `${year}-12-31` }), ); // 所有count相关的useQuery做同样修改 const schedulePatientCountQuery = useQuery('schedulePatientCount', () => schedule.SchedulePatientCountGetAllLite({ machineCategory: MachineCategory.Analysiss, }), ); const schedulePatientCountiodinQuery = useQuery('schedulePatientCountiodin', () => schedule.SchedulePatientCountGetAllLite({ machineCategory: MachineCategory.RadioTherapyByIodine, }), ); // ... 其他count的useQuery按此方式修改
2. 计算全局加载状态
创建变量判断所有API请求是否完成加载:
// 检查所有查询是否处于加载状态 const isGlobalLoading = chartsQueryTopographyDataQuery.isLoading || chartsQueryCityDataQuery.isLoading || chartsQueryAgeDataQuery.isLoading || schedulePatientCountQuery.isLoading || schedulePatientCountiodinQuery.isLoading || schedulePatientCountCtQuery.isLoading || schedulePatientCountGammaQuery.isLoading || schedulePatientCountMriQuery.isLoading || schedulePatientCountXrayQuery.isLoading || schedulePatientCountcesiumQuery.isLoading || schedulePatientCountCobaltQuery.isLoading || schedulePatientCountLinacQuery.isLoading;
3. 用Antd Spin组件包裹页面内容
将整个Dashboard内容用Spin组件包裹,根据isGlobalLoading控制显示:
return ( <Spin spinning={isGlobalLoading} tip="数据加载中..."> <div className='dashdoard-donutData'> {/* 原页面所有内容:欢迎卡片、BarCharts、PieCharts等 */} <Row> <Card className='card-welcome'> {/* ... 原欢迎卡片内容 */} </Card> </Row> <BarCharts xytotalTopography={xytotalTopography} xtopography={xtopography} xyAge={xyAge} /> <PieCharts xyCitytotalTopography={xyCitytotalTopography} xyTopographyPercent={xyTopographyPercent} analysisCountPatient={analysisCountPatient} ctCountPatient={ctCountPatient} mriCountPatient={mriCountPatient} gammaCountPatient={gammaCountPatient} xrayCountPatient={xrayCountPatient} cesiumCountPatient={cesiumCountPatient} cobaltCountPatient={cobaltCountPatient} iodinCountPatient={iodinCountPatient} linacCountPatient={linacCountPatient} year={year} /> </div> </Spin> );
4. 调整数据获取逻辑
因为现在保留的是完整query对象,后续使用数据时要改为从.data属性获取:
useEffect(() => { const mappedData1 = cleanData1(chartsQueryTopographyDataQuery.data); const mappedData2 = cleanData2(chartsQueryTopographyDataQuery.data); const mappedData3 = cleanData2(chartsQueryAgeDataQuery.data); const mappedData4 = cleanData3(chartsQueryCityDataQuery.data); const mappedData5 = cleanData3(chartsQueryTopographyDataQuery
相关产品推荐
相关产品推荐

