reactjs useEffect无限循环当我使用数组

jjjwad0x  于 2023-04-20  发布在  React
关注(0)|答案(2)|浏览(121)

我有一个包含多个接口的项目,在这些接口中有一个用于在Dashboard中显示统计数据的接口。
但是我遇到了一个问题,就是死循环,死循环的原因是什么,怎么解决?
此文件显示 Jmeter 板,并包含BarChart调用。
这个错误在我看来:

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;

该文件显示BarChart,并在内部调用条形图的API:
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>
                                        {' '}
                                        {'\u00a0\u00a0'}{' '}
                                        <FormattedMessage id='today' />
                                        {date}
                                    </Col>
                                </Row>
                            </Col>
                        </Row>
                    </Card>
                </Row>

                {/* Close Date Appointment*/}
                <ClosestFreeAppointment />

                {/* Bar Charts */}
                <BarCharts />

                {/* Pie Charts */}
                <PieCharts />
            </div>
        </>
    );
};

export default Dashboard;
vaj7vani

vaj7vani1#

问题是,在每次重新渲染时,useQuery将返回一个带有新引用的新对象,并且该对象被包含在useEffect的dependencies数组中,从而使其再次触发,设置其中的其他状态,触发另一次重新渲染,等等......
因为你只是使用useEffect钩子来根据useQuery返回的对象的data属性设置一些状态,所以你有几个选项。其中一个(不推荐,参考this article)是在数据可用时使用useQueryonSuccess选项来设置这些状态:

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.
    }
  }
)

理想的解决方案是从这三个状态中进行处理(xytotalTopographyxtopographyxyAge)并在渲染期间计算这些值。(从您的代码来看,它们似乎是常量,并且没有包含在queryKey数组中)。
看起来你现在使用的是react-query v3。顺便说一句,react-query目前的稳定版本是v4,v5即将发布。我建议你更新一下。

ercv8c1e

ercv8c1e2#

我认为你在错误的地方设置了状态,请尝试在Map之外进行。我还建议将map更改为forEach。还可以考虑只运行一次chartsQueryTopographyData循环。
注意:我不知道这是否是原因,因为你没有提供一个工作的例子.但你可以试试这个:

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]);

相关问题