skip to Main Content

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


  1. 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 for forEach. 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:

    useEffect(() => {
      // x = topography, y= male, y1= female
      chartsQueryTopographyData?.data?.forEach((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);
        }
        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);
        }
      });
      setxytotalTopography(xytTopography);
      setXTopography(topography);
    
      // x= age y= yTotal
      chartsQueryAgeData?.data?.forEach((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);
    }, [chartsQueryTopographyData, chartsQueryAgeData]);
    
    Login or Signup to reply.
  2. The issue is that on each re-render, useQuery will return a new object with a new reference, and this object is being included in useEffect‘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 on useQuery‘s returned object’s data property, you have a couple of options. One of them (not recommended, see this article for reference) is to use the onSuccess option of useQuery to set those states once the data is available:

    const chartsQueryTopographyData = useQuery(
      ['chartsQueryTopographyData'], 
      () => statisticCharts.GetTopographyStatistic({
            FilterType: 1,
            StartDate: `${year}-01-01`,
            EndDate: `${year}-12-31`
        }), 
      {
        onSuccess: (data) => {
          // Set states here with the query's data
          // This is not recommended.
        }
      }
    )
    

    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 which props is BarCharts 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 the queryKey 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search