最大更新深度警告反应

时间:2020-05-24 14:01:18

标签: javascript reactjs d3.js

我正在使用React在D3中创建一个图表。在我的图表中,我有一个操纵React Hooks的画笔组件。我有以下代码:-

App.js

import React, {useEffect, useState} from 'react';
import ChartWrapper from './ChartWrapper';
//import ;

function App() {
    let data = [{value: 5, time: 1590034669935}, {value: 4, time: 1590034670435}, {value: 7, time: 1590034670935}, {value: 10, time: 1590034671436}, {value: 4, time: 1590034671936}, {value: 6, time: 1590034672437}, {value: 7, time: 1590034672937}, {value: 4, time: 1590034673437}, {value: 7, time: 1590034673937}, {value: 5, time: 1590034674437}];
   // let [data, setData] = useState([]);
   const style = {
        textAlign: 'center',
    };


    return (
      <div className="App" style={style}>
        <ChartWrapper data={data}  xAxisText = 'Time' yAxisText = 'Values' />
      </div>
    );
  }

  export default App;

ChartWrapper.js

import React, {useRef, useEffect, useState, useImperativeHandle} from 'react';
import  {select, selectAll, extent, axisBottom, axisLeft, scaleLinear, scaleTime, curve, line, curveCardinal} from 'd3';
import {IsFirstRender} from './IsFirstRender';
import Chart from './Chart';
import NavChart from './NavChart';
import UsePrevious from './UsePrevious';

const MARGIN = {TOP: 50, BOTTOM: 50, LEFT: 50, RIGHT: 50};
const maxData = 10;
export default function ChartWrapper(props) {

    let [selection, setSelection] = useState([props.data[0].time, props.data[1].time]);
    const previousSelection = UsePrevious(selection);
   // let [isDefaultSelection, setDefaultSelection] = useState(true);
    const svgRef = useRef();
    let chart = null;
    let nav = null;
    const navRef = useRef();
    const isMount = IsFirstRender();


    useEffect(
        () => {
                console.log(selection);
                console.log(previousSelection);
                chart = new Chart(MARGIN, svgRef, props.data, props.xAxisText, props.yAxisText);
                nav = new NavChart(MARGIN, navRef, props.data, previousSelection.current, selection, setSelection);





        }, [props.data, previousSelection, selection]        
    );

    return (
        <div>
            <svg ref = {svgRef} width = {700} height = {400}></svg>
            <br /><br/>
            <svg ref = {navRef} width = {700} height = {75}></svg>
        </div>
    );
}

NavChart.js(这是我拥有画笔组件的地方)

import {select,
    selectAll,
    extent,
    axisBottom,
    axisLeft,
    scaleLinear,
    scaleTime,
    line,
    brushX,
    event
} from 'd3';

const PADDING = {TOP: 10, BOTTOM: 30};

export default class NavChart {
    constructor(MARGIN, svgRef, data, previousSelection, selection, setSelection) {

        this.svg = select(svgRef.current);
        this.MARGIN = MARGIN;
        this.data = data;

        this.chartWidth = this.svg.attr('width') - MARGIN.LEFT - MARGIN.RIGHT;
        this.chartHeight = this.svg.attr('height') - PADDING.TOP - PADDING.BOTTOM;

        this.xScale = scaleTime()
                        .domain(extent(
                            this.data.map((d) => d.time)
                        ))
                        .range([0, this.chartWidth]);

        this.yScale = scaleLinear()
                        .domain(extent(
                            this.data.map((d) => d.value)
                        ))
                        .range([this.chartHeight, 0]);

        this.myLine = line()
                        .x((d) => this.xScale(d.time))
                        .y((d) => this.yScale(d.value));

        this.chartArea = this.svg.append('g')
                        .attr('transform', `translate(${MARGIN.LEFT}, ${PADDING.TOP})`);

        this.chartArea.append('rect')
                        .attr('x', 0)
                        .attr('y', 0)
                        .attr('width', this.chartWidth)
                        .attr('height', this.chartHeight)
                        .attr('fill', '#f2f2f2');

        this.pathsG = this.chartArea.append('g')
                        .attr('class', 'pathsG')
                        .attr('transform', `translate(0,0)`);

        this.xAxis = axisBottom(this.xScale);

        this.xAxisG = this.chartArea.append('g')
                                       .attr('transform', `translate(0, ${this.chartHeight})`);

        this.xAxisG.call(this.xAxis);

        this.pathsG.selectAll('path')
            .data([this.data])
            .join('path')
            .attr('d', (value) => this.myLine(value))
            .attr('fill', 'none')
            .attr('stroke', '#D073BA')
            .attr('stroke-width', '1.5');

        this.circlesVar = this.pathsG.selectAll('.circle')
            .data(data);

        this.circlesVar.enter().append('circle')
            .attr('class', 'circle')
            .attr('r', (value) => 
                    value.time > selection[0] && value.time <= selection[1] ? 4 : 2)
            .style('fill', '#D073BA')
            .attr('cx', (d) => this.xScale(d.time))
            .attr('cy', (d) => this.yScale(d.value));

        this.brushG = this.chartArea.append('g')
                        .attr('class', 'brush');

        this.brush = brushX().extent([
            [0, 0],
            [this.chartWidth, this.chartHeight]
        ]).on('start brush end', () => {
            if (event.selection) {
                const timeSelection = event.selection.map(this.xScale.invert);
                setSelection(timeSelection);     // This is where I suspect the error is?
            }
        });

        if (previousSelection === selection) {
            console.log("true");
            this.brushG.call(this.brush)
                        .call(this.brush.move, selection.map(this.xScale));
        }
    }
}

UsePrevious.js(用于跟踪变量状态的功能组件)

import {useEffect, useRef} from 'react';

function UsePrevious(value) {
    const ref = useRef();
    useEffect( () => {
        ref.current = value;
    });
    return ref;
}

export default UsePrevious;

运行代码后,图表在浏览器中可以很好地加载,但是变得非常缓慢,我在控制台中收到此错误-“

StackTrace->

警告:超过最大更新深度。当组件在useEffect中调用setState时会发生这种情况,但useEffect要么没有依赖项数组,要么在每个渲染器上都有一个依赖项更改。

在ChartWrapper中(在App.js:15)
在div中(在App.js:14)
在App中(位于src / index.js:10)
在StrictMode中(在src / index.js:9处)index.js:1
e index.js:1

反应5
-> printWarning
->错误
-> checkForNestedUpdates
-> scheduleUpdateOnFiber
-> dispatchAction

刷NavChart.js:90(这是“ setSelection(timeScale)”行)
套用dispatch.js:61
customEvent on.js:103
发出brush.js:320
开始brush.js:307
移动brush.js:251
默认为each.js:5
移动brush.js:240
默认的call.js:4
NavChart NavChart.js:96(这是“ this.brushG.call(this.brush)”行)
ChartWrapper ChartWrapper.js:29(这行是'nav = new NavChart(MARGIN,navRef,props.data,previousSelection.current,selection,setSelection);')

反应6
-> commitHookEffectListMount
-> commitPassiveHookEffects
-> callCallback
-> invokeGuardedCallbackDev
-> invokeGuardedCallback
-> flushPassiveEffectsImpl
stable_runWithPriority scheduler.development.js:653

反应3
-> runWithPriority $ 1
-> flushPassiveEffects
-> commitBeforeMutationEffects
workLoop scheduler.development.js:597
flushWork scheduler.development.js:552
performWorkUntilDeadline scheduler.development.js:164

现在,我了解到这可能是由于无限循环所致,每次useEffect依赖关系更改时都会渲染我的组件,但是我不知道何时发生。我唯一更改依赖项的地方是在navChart组件内调用setSelection(timeSelection)的行中。另外,该行仅应在更改笔刷时运行。

我在做什么错?

2 个答案:

答案 0 :(得分:0)

您应该将依赖关系传递给useEffect,否则它将被称为发生任何更改

import {useEffect, useRef} from 'react';

function UsePrevious(value) {
    const ref = useRef();
    useEffect( () => {
        ref.current = value;
    }, [value]);
    return ref;
}

export default UsePrevious;

答案 1 :(得分:0)

因此,我找到了解决问题的方法。当我调用“ on()”方法时,在“ NavChart.js”中,if语句为:

if (event.selection)

每次上线时都会调用它

this.brushG.call(this.brush)

被调用。这是因为事件侦听器不仅侦听通过鼠标完成的笔刷更改,而且还侦听以编程方式完成的更改,我想避免这种情况。通过将if语句更改为:

if(event.selection && event.sourceEvent)

您摆脱了这个问题,因为由于编程更改而调用事件时event.sourceEvent为null。仅当事件由于外部事件(例如鼠标移动)而触发时,sourceEvent才会有一个值。