我对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_type
和balance
,另一个使用account_type
和count
。
在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)〉组件中...
另一个尝试是,我在组件中进行所有计算,但不使用setState
和useEffect
,结果如下所示:
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 */
...
这段代码的结果是一个渲染的甜甜圈,其中的数据是正确的,但中心值总是0
和0 M
。我在这里添加了一个控制台日志,以查看这些值发生了什么,我看到了两次渲染,因此每个组件示例都有两次渲染。每个组件的第一个值总是0,第二个渲染是正确的值。
所以请帮助我的花生大脑明白我把事情搞砸了...提前感谢。
1条答案
按热度按时间gc0ot86w1#
DataPie组件中的useEffect没有依赖项,这意味着它将在每次呈现时运行。并且,useEffect本身会导致组件重新呈现(通过调用useState)。因此,在此处创建了一个无限的重新呈现。