React ChartJS 防止新数据在重绘后被添加到状态中?

Posted

技术标签:

【中文标题】React ChartJS 防止新数据在重绘后被添加到状态中?【英文标题】:React ChartJS prevent new data from being added into state after it's redrawn? 【发布时间】:2019-02-22 20:33:57 【问题描述】:

我正在尝试使用 react-chartjs-2 更新我的图表。我正在使用日期选择器来过滤不同的数据并相应地重新渲染图表,例如显示今天、昨天、过去 7 天的数据等。正在从我的数据库中获取数据

但是,当图表被重绘并重新渲染时,它会被添加到我不想要的状态中。我只是想重新渲染请求重新渲染的新数据并添加到图表中的旧数据中。

实际上我之前使用 vanilla javascript 解决了这个问题,因为我没有使用 react 我使用了图表文档说要使用的 destroy() 方法,但我不确定如何将它与 react 一起使用?

所以经过进一步检查,我的图表似乎重新渲染得很好。然而,当它被重新渲染时,额外的数据被添加到我不想要的我的 chartData 状态,我只希望被请求的新数据显示在图表上。我仍在试图弄清楚那部分。

这是我的代码,我有很多,所以我只显示相关部分:

import React from "react";

import reportsService from "../../services/reportsService";
import update from "react-addons-update";
import moment from "moment";

import  Bar  from "react-chartjs-2";
import "chartjs-plugin-labels";
import "chartjs-plugin-datalabels";

class Reportspage extends React.Component 
  constructor(props) 
    super(props);

    this.state = 
      chartData: 
        labels: [],
        datasets: [
          
            //label: "Quotes",
            data: [],
            backgroundColor: []
          
        ]
      
    ;
  

  chartColors() 
    let colors = [];
    for (let i = 0; i < 100; i++) 
      let r = Math.floor(Math.random() * 200);
      let g = Math.floor(Math.random() * 200);
      let b = Math.floor(Math.random() * 200);
      let c = "rgb(" + r + ", " + g + ", " + b + ")";
      colors.push(c);
    

    // Update deep nested array of objects in state
    this.setState(
      chartData: update(this.state.chartData, 
        datasets:  0:  backgroundColor:  $set: colors   
      )
    );
  

  datePicker() 
    let _this = this;
    let start = moment().subtract(29, "days");
    let end = moment();
    let showReports;
    let data;
    let reloNames = [];
    let reloCount = [];

    function focusDate(start, end) 
      $("#daterangePicker span").html(
        start.format("MMMM D, YYYY") + " - " + end.format("MMMM D, YYYY")
      );
    

    $("#daterangePicker").daterangepicker(
      
        startDate: start,
        endDate: end,
        ranges: 
          Today: [moment(), moment()],
          Yesterday: [
            moment().subtract(1, "days"),
            moment().subtract(1, "days")
          ],
          "Last 7 Days": [moment().subtract(6, "days"), moment()],
          "Last 30 Days": [moment().subtract(29, "days"), moment()],
          "This Month": [moment().startOf("month"), moment().endOf("month")],
          "Last Month": [
            moment()
              .subtract(1, "month")
              .startOf("month"),
            moment()
              .subtract(1, "month")
              .endOf("month")
          ]
        
      ,
      focusDate
    );

    focusDate(start, end);

    $("#daterangePicker").on("apply.daterangepicker", async function(
      event,
      picker
    ) 
      switch (picker.chosenLabel) 
        case "Today":
          showReports = await reportsService.reloQuotes(
            reportStatus: "Today"
          );

          data = showReports.recordsets[0];

          data.forEach((element, index, array) => 
            reloNames.push(element.reloNames);
            reloCount.push(element.NoofOrders);
          );

          _this.setState(
            chartData: update(_this.state.chartData, 
              labels:  $set: reloNames ,
              datasets:  0:  data:  $set: reloCount   
            )
          );

          console.log(_this.state);

          break;
        case "Yesterday":
          showReports = await reportsService.reloQuotes(
            reportStatus: "Yesterday"
          );

          data = showReports.recordsets[0];

          data.forEach((element, index, array) => 
            reloNames.push(element.reloNames);
            reloCount.push(element.NoofOrders);
          );

          _this.setState(
            chartData: update(_this.state.chartData, 
              labels:  $set: reloNames ,
              datasets:  0:  data:  $set: reloCount   
            )
          );

          console.log(_this.state);

          break;
        case "Last 7 Days":
          showReports = await reportsService.reloQuotes(
            reportStatus: "Last 7 Days"
          );

          data = showReports.recordsets[0];

          data.forEach((element, index, array) => 
            reloNames.push(element.reloNames);
            reloCount.push(element.NoofOrders);
          );

          _this.setState(
            chartData: update(_this.state.chartData, 
              labels:  $set: reloNames ,
              datasets:  0:  data:  $set: reloCount   
            )
          );

          console.log(_this.state);

          break;
      
    );

    //console.log(this.state);
  

  async reloQuotes() 
    const showreloQuotes = await reportsService.reloQuotes();
    let data = showreloQuotes.recordsets[0];
    let reloNames = [];
    let reloCount = [];

    data.forEach((element, index, array) => 
      reloNames.push(element.reloNames);
      reloCount.push(element.NoofOrders);
    );

    this.setState(
      chartData: update(this.state.chartData, 
        labels:  $set: reloNames ,
        datasets:  0:  data:  $set: reloCount   
      )
    );
  

  async componentDidMount() 
    await this.chartColors();
    await this.datePicker();

    // Execute models real time thus re-rendering live data on the chart real time
    await this.reloQuotes();
  

  render() 
    return (
      <div className="fluid-container">
        <div className="container">
          <h1>Reports</h1>
          <div className="row">
            <div className="daterangeContainer">
              <div
                id="daterangePicker"
                style=
                  background: "#fff",
                  cursor: "pointer",
                  padding: "5px 10px",
                  border: "1px solid #ccc",
                  width: "100%"
                
              >
                <i className="fa fa-calendar" />
                &nbsp;
                <span /> <i className="fa fa-caret-down" />
              </div>
            </div>
          </div>
          <div className="row">
            <div className="col-md-12">
              <Bar
                data=this.state.chartData
                height=800
                options=
                  maintainAspectRatio: false,
                  legend: 
                    display: false
                  ,
                  scales: 
                    xAxes: [
                      
                        ticks: 
                          beginAtZero: true,
                          autoSkip: false
                        ,
                        scaleLabel: 
                          display: true
                        
                      
                    ]
                  ,
                  title: 
                    display: true,
                    text: "Quotes",
                    fontSize: 16
                  ,
                  plugins: 
                    datalabels: 
                      display: true,
                      color: "white"
                    
                  
                
                redraw
              />
            </div>
          </div>
        </div>
      </div>
    );
  


export default Reportspage;

【问题讨论】:

【参考方案1】:

所以你描述的问题是当组件重新渲染时数据被添加到你的组件状态中。在您提供的代码片段中,您不使用 React 中可以在重新渲染时触发的任何生命周期方法。而且我看不到任何其他应该在重新渲染时触发的钩子。因此,我找不到您的问题的任何根源。

但是,我可以看到其他可能使您更难调试的问题。解决这些问题可能会帮助您确定实际问题。在componentDidMount 方法中,您调用其唯一目的是更新状态的函数。这不是一个好的设计,因为它会强制组件在安装时立即重新渲染几次。

更好的设计是在构造函数中充分准备chartData-objekt。例如,您可以更改 chartColors 函数以将 chartData 对象作为参数,并返回添加了颜色的新对象。然后让你的构造函数看起来像这样:

constructor(props) 

   super(props);
   const chartDataWithoutColors = 
       labels: [],
       datasets: [
         
           //label: "Quotes",
           data: [],
           backgroundColor: []
         
       ]
     

   const chartDataWithColor = this.chartColors(chartDataWithoutColors);

   this.state = 
     chartData: chartDataWithColor
   ;

通过删除对setState 的不必要调用,您将使您的组件生活更简单,性能更高。当你简化了你的组件后,开始调试,一次删除一个非关键部分,并在你的问题消失时尝试确定。这应该足以找到错误。

祝你好运!

【讨论】:

好的,我将尝试使用不同的生命周期,而不是在安装到组件时立即设置状态。谢谢你,我会尝试不同的方法,并就此与你联系。 所以我认为我需要对我的问题进行更多研究以解决我遇到的问题。我要尝试的是完全删除我现在拥有的所有代码并尝试一种不同的方法。我不知道如何结束这个赏金,但我会看看我是否可以尝试不同的方法。感谢您的建议,我会在以后使用它。

以上是关于React ChartJS 防止新数据在重绘后被添加到状态中?的主要内容,如果未能解决你的问题,请参考以下文章

TreeView重绘后 水平滚动条问题

Today Extension 视图在重绘时闪烁

如何(自动)测试浏览器重绘后是不是发生操作?

WinForm防止界面闪烁

TabView 在重绘时是不是会错过导航点击?

“onscroll”在重绘之后还是之前触发?