I have a project that contains several interfaces, and among these interfaces there is an interface for displaying statistics in Dashboard.
But I encountered a problem which is the infinite loop, what is the cause of the infinite loop and how can I solve it?
This file displays the dashboard, and contains the BarChart
call.
And this error appears to me:
Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
BarChart.tsx
:
import { Row, Col, Card } from "antd";
import { FunctionComponent, useEffect, useState } from "react";
import Chart from "react-google-charts";
import { FormattedMessage } from "react-intl";
import { useQuery } from "react-query";
import statisticCharts from '../../../../api/nuclearMedicineApi/services/Statistic';
import { options_3 } from "../chartsOptions";
interface BarChartsProps { }
interface ValueDto {
x: any,
yMale: any,
yFemale: any
}
const BarCharts: FunctionComponent<BarChartsProps> = () => {
var today = new Date();
var year = today.getFullYear();
const chartsQueryTopographyData = useQuery(['chartsQueryTopographyData'], () =>
statisticCharts.GetTopographyStatistic({ FilterType: 1, StartDate: `${year}-01-01`, EndDate: `${year}-12-31` }),
);
const chartsQueryAgeData = useQuery(['chartsQueryAgeData'], () =>
statisticCharts.GetTopographyStatistic({ FilterType: 3, StartDate: `${year}-01-01`, EndDate: `${year}-12-31` }),
);
var xytTopography: any[] = [['topography', 'ذكر', 'أنثى']];
var topography: any[] = [['topography', 'المرضى المصابين']];
var ageBar: any[] = [['age', 'المرضى المصابين']];
const [xytotalTopography, setxytotalTopography] = useState<any[]>([])
const [xtopography, setXTopography] = useState<any[]>([]);
const [xyAge, setxyAge] = useState<any[]>([])
useEffect(() => {
// x = topography, y= male, y1= female
chartsQueryTopographyData?.data?.map((xy: any, index: any) => {
if (xy?.x !== undefined && xy?.yMale !== undefined && xy?.yFemale !== undefined) {
let x1: any = xy.x;
let y: any = xy.yMale;
let y1: any = xy.yFemale;
let xyData: any[] = [x1, y, y1];
xytTopography.push(xyData);
setxytotalTopography(xytTopography)
return xytTopography;
}
})
// x=topography, y=yTotal
chartsQueryTopographyData?.data?.map((xy: any, index: any) => {
if (xy?.x !== undefined && xy?.yTotal !== undefined) {
let x1: any = xy.x;
let y: any = xy.yTotal;
let xyData: any[] = [x1, y]
topography.push(xyData);
setXTopography(topography)
return topography;
}
})
// x= age y= yTotal
chartsQueryAgeData?.data?.map((xy: any, index: any) => {
if (xy?.x !== undefined && xy?.yTotal !== undefined) {
let x1: any = xy.x;
let y: any = xy.yTotal;
let xyData: any[] = [x1, y]
ageBar.push(xyData);
setxyAge(ageBar)
return ageBar;
}
})
}, [chartsQueryTopographyData, chartsQueryAgeData]);
return (<>
{/* bar chart topography with male and female */}
<Row>
<Col className='mt-4' lg={24}>
<Card className='card-chart card-bar-chart'>
<Row>
<Col lg={19}>
<h3>
<FormattedMessage id='number-of-cancer-cases-by-male-female' />{' '}
{year}{' '}
</h3>
</Col>
</Row>
<Row className='mt-8'>
<Col lg={24}>
<Chart
chartType="BarChart"
width="100%"
height="400px"
data={xytotalTopography}
options={options_3}
/>
</Col>
</Row>
</Card>
</Col>
</Row>
<Row>
{/* bar chart x=topography with y=yTotal*/}
<Col className='mt-4' lg={24}>
<Card className='card-chart card-bar-number-chart'>
<Row>
<Col lg={19}>
<h3>
{' '}
<FormattedMessage id='number-of-cancer-cases-by-number' />{' '}
{year}{' '}
</h3>
</Col>
</Row>
<Row className='mt-8'>
<Col lg={24}>
<Chart
chartType="BarChart"
width="100%"
height="400px"
data={xtopography}
options={options_3}
/>
</Col>
</Row>
</Card>
</Col>
</Row>
<Row>
{/* bar chart x=topography with y=Age*/}
<Col className='mt-4' lg={24}>
<Card className='card-chart card-bar-age-chart'>
<Row>
<Col lg={19}>
<h3>
{' '}
<FormattedMessage id='number-of-cancer-cases-by-age' />{' '}
{year}{' '}
</h3>
</Col>
</Row>
<Row className='mt-8'>
<Col lg={24}>
<Chart
chartType="BarChart"
width="100%"
height="400px"
data={xyAge}
options={options_3}
/>
</Col>
</Row>
</Card>
</Col>
</Row>
</>)
}
export default BarCharts;
This file displays the BarChart
and calls the barchart’s APIs inside:
DashboardChart.tsx
:
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 ClosestFreeAppointment from './charts/closestFreeAppointment';
import BarCharts from './charts/barCharts';
import PieCharts from './charts/pieCharts';
interface DashboardProps { }
const Dashboard: FunctionComponent<DashboardProps> = () => {
const current = new Date();
const date = `${current.getFullYear()}/${current.getMonth() + 1
}/${current.getDate()}`;
const auth = useContext<IAuthContext>(AuthContext);
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>
{' '}
{'u00a0u00a0'}{' '}
<FormattedMessage id='today' />
{date}
</Col>
</Row>
</Col>
</Row>
</Card>
</Row>
{/* Close Date Appointment*/}
<ClosestFreeAppointment />
{/* Bar Charts */}
<BarCharts />
{/* Pie Charts */}
<PieCharts />
</div>
</>
);
};
export default Dashboard;
2
Answers
I think you are setting the state in the wrong place, please try to do it outside the map. I also recommend to change
map
forforEach
. Also consider running the chartsQueryTopographyData loop only one time.Note: I do not know if this is the reason since you did not provide a working example. But you can try this:
The issue is that on each re-render,
useQuery
will return a new object with a new reference, and this object is being included inuseEffect
‘s dependencies array, thus making it fire again, set the other states inside it, triggering another re-render, and so on…Since you’re just using the
useEffect
hook to set some states based onuseQuery
‘s returned object’sdata
property, you have a couple of options. One of them (not recommended, see this article for reference) is to use theonSuccess
option ofuseQuery
to set those states once the data is available:The ideal solution would be to dispose from those 3 states (
xytotalTopography
,xtopography
,xyAge
) and calculate those values during rendering. Also, it’s not clear to me whichprops
isBarCharts
supposed to receive and if the query should get triggered again each time the query function’s arguments change (from your code, they seem to be constant and they are not being included in thequeryKey
array).It seems you’re currently using react-query v3. Just for the record, react-query’s current stable version is v4 and v5 is soon to be released. I suggest you to update it.