ChartJS React:可重用组件不断失败(渲染过多|值未显示|未捕获错误|......)

8fsztsew  于 2022-11-23  发布在  Chart.js
关注(0)|答案(1)|浏览(97)

我对react和Javascript还比较陌生。我试图用chartjs创建一个甜甜圈组件,但是在尝试了两天不同的方法之后,我没有成功地生成我想要的甜甜圈。我的想法是让一个甜甜圈的和值位于中间。非常简单,我想。用正确的标签和值渲染甜甜圈我很容易就做到了。让我夜不能寐的问题是中间的值。以下是通过属性传递的数据:

[
    {
        "account_type": "basic",
        "balance": 96548852,
        "count": 1529
    },
    {
        "account_type": "miner",
        "balance": 34532244,
        "count": 9466
    },
    {
        "account_type": "validator",
        "balance": 726277542,
        "count": 135
    },
    {
        "account_type": "community",
        "balance": 710828589,
        "count": 14
    }
]

这个数据集我打算用于两个不同的圆环图。一个使用account_typebalance,另一个使用account_typecount
App.jsx中,代码看起来非常简单。我的api提供数据,这些数据已经过测试和确认。问题是呈现图表。App.jsx如下:

import React, { Component } from "react";
import axios from "axios";
import { Route, Routes } from "react-router-dom";

import DataPie from "./components/DataPie";

class App extends Component {
  constructor() {
    super();

    this.state = {
      dataSet: [],
    };
  }

  componentDidMount() {
    this.getDataSet();
  }

  getDataSet = () => {
    axios
      .get(`${process.env.REACT_APP_API_SERVICE_URL}/dataSet`)
      .then((res) => {
        this.setState({ dataSet: res.data });
      })
      .catch((err) => {
        console.log(err);
      });
  };

  render() {
    return (
      <div>
        <section className="section">
          <div className="container">
            <div className="container is-max-desktop">
              <Routes>
                <Route 
                  exact 
                    path="/balances" 
                    element={
                      <div className="columns">
                        <div className="column box">
                          <DataPie 
                            dataSet={this.state.dataSet} /* same data set is passed */
                            labelElem='account_type'     /* labels for donut*/
                            valueElem='balance'          /* values for donut */
                            divider='1000000'            /* center value divider */
                            suffix=' M'                  /* suffix for center value */
                          />
                        </div>
                        <div className="column box">
                          <DataPie 
                            dataSet={this.state.dataSet} /* same data set is passed */
                            labelElem='account_type'     /* labels for donut */
                            valueElem='count'            /* values for donut */
                          />
                        </div>
                      </div>
                    }
                  />
            </div>
          </div>
        </section>
      </div>
    );
  }
}

export default App;

而我已经干预了两天的东西,组成部分:

import React, { useState, useEffect } from "react";
import PropTypes from "prop-types";
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js';
import { Doughnut } from 'react-chartjs-2';

ChartJS.register(ArcElement, Tooltip, Legend);

const DataPie = (props) => {
  const [data, setData] = useState({});
  const [plugins, setPlugins] = useState([]);

  useEffect(() => {
    let labels = [];
    let values = [];  

    props.dataSet.forEach(element => {
      labels.push(element[props.labelElem]);
      values.push(element[props.valueElem]);
    });

    setData({
      labels: labels,
      datasets: [
        {
          data: values,
          backgroundColor: [
            "rgba(255, 99, 132, 0.8)",
            "rgba(54, 162, 235, 0.8)",
            "rgba(255, 206, 86, 0.8)",
            "rgba(75, 192, 192, 0.8)",
            "rgba(153, 102, 255, 0.8)",
            "rgba(255, 159, 64, 0.8)"
          ],
          borderColor: [
            "rgba(255, 99, 132, 1)",
            "rgba(54, 162, 235, 1)",
            "rgba(255, 206, 86, 1)",
            "rgba(75, 192, 192, 1)",
            "rgba(153, 102, 255, 1)",
            "rgba(255, 159, 64, 1)"
          ],
          borderWidth: 1,
          polyline: {
            color: "gray",
            labelColor: "gray",
            formatter: (value) => `formatted ${value}`
          }
        }
      ]
    });

    let suffix = (props.suffix ? props.suffix : '' );
    let divider = (props.divider ? parseInt(props.divider) : 1 );
    let valueSum = 0;
    props.dataSet.forEach(element => {
      valueSum = valueSum + element[props.valueElem];
    });

    setPlugins([
      {
        beforeDraw: function (chart) {
          var width = chart.width,
            height = chart.height,
            ctx = chart.ctx;
          ctx.restore();
          var fontSize = (height / 160).toFixed(2);
          ctx.font = fontSize + "em sans-serif";
          ctx.textBaseline = "top";
          var text = Math.round(valueSum / divider).toString() + suffix,
            textX = Math.round((width - ctx.measureText(text).width) / 2),
            textY = height / 2;
          ctx.fillText(text, textX, textY);
          ctx.save();
        }
      }
    ]);
  }, [props]);

  const options = {
    plugins: {
      legend: { 
        display: false
      }
    }
  };

  return (
    <Doughnut 
      data={data}
      options={options} 
      plugins={plugins}
    />
  );
}

DataPie.propTypes = {
    dataSet: PropTypes.array.isRequired,
    labelElem: PropTypes.string.isRequired,
    valueElem: PropTypes.string.isRequired,
};

export default DataPie;

当我添加圆环图的插件部分时,问题就开始出现了。在该插件部分中,我计算图表中所有值的总和,并尝试将它们显示在圆环图的中心。此代码有效,因为我可以显示静态值。但当我尝试添加计算值时,它从未显示在图表上。
在我最后一次尝试时,我开始使用{ useState, useEffect },但现在我的甜甜圈甚至不再渲染,出现了各种错误,如:

  • 错误:重新呈现太多。React限制了重新呈现的次数...
  • 未捕获的类型错误:无法读取undefined的属性(阅读'map')
  • 以上错误发生在〈ForwardRef(ChartComponent)〉组件中...

另一个尝试是,我在组件中进行所有计算,但不使用setStateuseEffect,结果如下所示:

const DataPie = (props) => {
  const labels = [];
  const values = [];
  let valueSum = 0;
  let suffix = (props.suffix ? props.suffix : '' );
  let divider = (props.divider ? parseInt(props.divider) : 1 );

  props.dataSet.forEach(element => {
    labels.push(element[props.labelElem]);
    values.push(element[props.valueElem]);
    valueSum = valueSum + element[props.valueElem];
  });

  console.log(valueSum);

  const data = [...]     /* < labels and values where inserted here */
  const options = {...}
  const plugins = {...}  /* < valueSum is inserted here */
  
  ...

这段代码的结果是一个渲染的甜甜圈,其中的数据是正确的,但中心值总是00 M。我在这里添加了一个控制台日志,以查看这些值发生了什么,我看到了两次渲染,因此每个组件示例都有两次渲染。每个组件的第一个值总是0,第二个渲染是正确的值。
所以请帮助我的花生大脑明白我把事情搞砸了...提前感谢。

gc0ot86w

gc0ot86w1#

DataPie组件中的useEffect没有依赖项,这意味着它将在每次呈现时运行。并且,useEffect本身会导致组件重新呈现(通过调用useState)。因此,在此处创建了一个无限的重新呈现。

相关问题