如何在饼图中以指针显示动态数据

huangapple go评论70阅读模式
英文:

How to show dynamic data in a pie chart with needle

问题

Meter 组件中,你正在尝试使用 value 变量进行映射,而 value 是一个数组,这可能导致错误。你可以尝试修改 value 的赋值方式,确保它是一个数字,而不是包含数据的数组。例如,你可以使用 conData[0].data

const value = conData[0].data;

或者,如果你想显示所有州的总能耗,你可能需要在 Calculation 组件中对所有选定州的能耗数据进行求和,然后将总和传递给 Meter 组件。

另外,确保 conData 不是 undefinednull,可以在 Meter 组件中添加一个条件来检查:

const value = conData ? conData[0].data : 0;

这样可以避免在 conDataundefined 时引发错误。

英文:

I am trying to make a pie chart with needle with recharts. Link. Actually I am selecting a state name from a dropdown and wanting to show the selected state's energy consumption data in the pie chart via needle. So here is my states.json file:

[
    {
        "state_id": "1",
        "sortname": "AF",
        "state_name": "ANDAMAN & NICOBAR ISLANDS",
        "energyData": [
            {
                "data_id": "1",
                "data": "367.3948635",
                "state_id": "1"
            }
        ]
    },
    {
        "state_id": "2",
        "sortname": "DZ",
        "state_name": "ANDHRA PRADESH",
        "energyData": [
            {
                "data_id": "2",
                "data": "328.8017687",
                "state_id": "2"
            }
        ]
    },
    {
        "state_id": "3",
        "sortname": "AS",
        "state_name": "ARUNACHAL PRADESH",
        "energyData": [
            {
                "data_id": "3",
                "data": "120.9100817",
                "state_id": "3"
            }
        ]
    },
    {
        "state_id": "4",
        "sortname": "AD",
        "state_name": "ASSAM",
        "energyData": [
            {
                "data_id": "4",
                "data": "105.9831799",
                "state_id": "4"
            }
        ]
    },
    {
        "state_id": "5",
        "sortname": "AO",
        "state_name": "CHANDIGARH",
        "energyData": [
            {
                "data_id": "5",
                "data": "564.5855402",
                "state_id": "5"
            }
        ]
    },
    {
        "state_id": "6",
        "sortname": "AI",
        "state_name": "BIHAR",
        "energyData": [
            {
                "data_id": "6",
                "data": "118.696485",
                "state_id": "6"
            }
        ]
    },
    {
        "state_id": "7",
        "sortname": "AQ",
        "state_name": "CHHATTISGARH",
        "energyData": [
            {
                "data_id": "7",
                "data": "213.6288618",
                "state_id": "7"
            }
        ]
    },
    {
        "state_id": "8",
        "sortname": "AG",
        "state_name": "DADRA & NAGAR HAVELI",
        "energyData": [
            {
                "data_id": "7",
                "data": "213.6288618",
                "state_id": "8"
            }
        ]
    },
    {
        "state_id": "9",
        "sortname": "AR",
        "state_name": "DAMAN & DIU ",
        "energyData": [
            {
                "data_id": "7",
                "data": "213.6288618",
                "state_id": "9"
            }
        ]
    },
    {
        "state_id": "10",
        "sortname": "AM",
        "state_name": "GOA",
        "energyData": [
            {
                "data_id": "7",
                "data": "213.6288618",
                "state_id": "10"
            }
        ]
    }
]

And here is my calculation page, where is the dropdown :

import React, { useState } from 'react';
import LetsGoButton from './LetsGoButton';
import statesData from '../../public/states.json'
import Meter from './Meter';

const Calculation = () => {
    const [statesId, setStatesId] = useState('')
    const [avgConsumptionData, setAvgConsumptionData] = useState([])
    const [conDataId, setConDataId] = useState('')


    const handleStates = (e) => {
        const getStateId = e.target.value
        const getAvgEnergyData = statesData.find(state => state.state_id === getStateId)?.energyData
        setAvgConsumptionData(getAvgEnergyData)
        setStatesId(getStateId)
    }


    const handleAvgConsumptionData = (e) => {
        const conDataId = e.target.value
        console.log(conDataId);
        setConDataId(conDataId)
    }


    return (
        <section>
            <div>

                <div>

                    {/*  STATE */}
                    <div>
                        <select defaultValue="State" name="state" id="state" onChange={(e) => handleStates(e)}>
                            <option selected disabled>State</option>
                            {
                                statesData.map((data, idx) => (
                                    <option value={data.state_id} key={idx}>{data.state_name}</option>
                                ))
                            }
                        </select>
                    </div>

                    <Meter conData={avgConsumptionData} />
                </div>
                <LetsGoButton />
            </div>
        </section>
    );
};

export default Calculation;

I passed the avgConsumptionData to the Meter component and tried to set the data dynamically in the needle value. Here is the Meter component :

import React, { useState } from 'react';
import { PieChart, Pie, Cell } from 'recharts';

const Meter = ({ conData}) => {
   console.log(conData);
    const RADIAN = Math.PI / 180;
    const data = [
        { name: '0', value: 0, color: '#ff0000' },
        { name: '150', value: 150, color: '#00ff00' },
        { name: '300', value: 300, color: '#0000ff' },
        { name: '450', value: 450, color: '#924f64' },
        { name: '600', value: 600, color: '#dc8965' },
        { name: '750', value: 750, color: '#d2e1f7' },
        { name: '900', value: 900, color: '#131b27' },
];
    const cx = 150;
    const cy = 200;
    const iR = 50;
    const oR = 100;
    const value = conData
    // const value = conData[0].data
    // const value = conData.map(x => x.data)
    const needle = (value, data, cx, cy, iR, oR, color) => {
        let total = 0;
        data.forEach((v) => {
            total += v.value;
        });
        const ang = 180.0 * (1 - value / total);
        const length = (iR + 2 * oR) / 3;
        const sin = Math.sin(-RADIAN * ang);
        const cos = Math.cos(-RADIAN * ang);
        const r = 5;
        const x0 = cx + 5;
        const y0 = cy + 5;
        const xba = x0 + r * sin;
        const yba = y0 - r * cos;
        const xbb = x0 - r * sin;
        const ybb = y0 + r * cos;
        const xp = x0 + length * cos;
        const yp = y0 + length * sin;

        return [
            <circle cx={x0} cy={y0} r={r} fill={color} stroke="none" />,
            <path d={`M${xba} ${yba}L${xbb} ${ybb} L${xp} ${yp} L${xba} ${yba}`} stroke="#none" />,
        ];
    };
    return (
        <div>
            <PieChart>
                <Pie
                    dataKey="value"
                    startAngle={180}
                    endAngle={0}
                    data={data}
                    cx={cx}
                    cy={cy}
                    innerRadius={iR}
                    outerRadius={oR}
                    fill="#8884d8"
                    stroke="none"
                >
                    {data.map((entry, index) => (
                        <Cell key={`cell-${index}`} fill={entry.color} />
                    ))}
                </Pie>
                {needle(value, data, cx, cy, iR, oR, '#d0d000')}
            </PieChart>
        </div>
    );
};

export default Meter;

So when I am logging in the "value" variable which contains the conData which is having the selected states data, I see an Array in the console that contains data_id, data, and state_id. But when I want to map the array an error occurs :

> TypeError: Cannot read properties of undefined (reading 'map')

why this error is occurring? Is the passing method of data from the calculation component to the Meter component correct or wrong? is there any other way to solve it?

答案1

得分: 0

I just changed the data type you were passing on your Meter component from Array to Object.

Calculation.js

const handleStates = (e) => {
    const getStateId = e.target.value
    const getAvgEnergyData = statesData.find(state => state.state_id === getStateId)?.energyData
    setAvgConsumptionData(getAvgEnergyData[0])
    setStatesId(getStateId)
}

Meter.js

const Meter = ({ conData}) => {
    console.log(conData);
    const RADIAN = Math.PI / 180;
    const data = [
        { name: '0', value: 0, color: '#ff0000' },
        { name: '150', value: 150, color: '#00ff00' },
        { name: '300', value: 300, color: '#0000ff' },
        { name: '450', value: 450, color: '#924f64' },
        { name: '600', value: 600, color: '#dc8965' },
        { name: '750', value: 750, color: '#d2e1f7' },
        { name: '900', value: 900, color: '#131b27' },
    ];
    const cx = 150;
    const cy = 200;
    const iR = 50;
    const oR = 100;
    const value = conData.data;
    // const value = conData[0].data
    // const value = conData.map(x => x.data)
    const needle = (value, data, cx, cy, iR, oR, color) => {
        let total = 0;
        data.forEach((v) => {
            total += v.value;
        });
        const ang = 180.0 * (1 - value / total);
        const length = (iR + 2 * oR) / 3;
        const sin = Math.sin(-RADIAN * ang);
        const cos = Math.cos(-RADIAN * ang);
        const r = 5;
        const x0 = cx + 5;
        const y0 = cy + 5;
        const xba = x0 + r * sin;
        const yba = y0 - r * cos;
        const xbb = x0 - r * sin;
        const ybb = y0 + r * cos;
        const xp = x0 + length * cos;
        const yp = y0 + length * sin;

        return [
            <circle cx={x0} cy={y0} r={r} fill={color} stroke="none" />,
            <path d={`M${xba} ${yba}L${xbb} ${ybb} L${xp} ${yp} L${xba} ${yba}`} stroke="#none" />,
        ];
    };
    return (
        <div>
                <PieChart width={400} height={500}>
                <Pie
                    dataKey="value"
                    startAngle={180}
                    endAngle={0}
                    data={data}
                    cx={cx}
                    cy={cy}
                    innerRadius={iR}
                    outerRadius={oR}
                    fill="#8884d8"
                    stroke="none"
                >
                    {data.map((entry, index) => (
                        <Cell key={`cell-${index}`} fill={entry.color} />
                    ))}
                </Pie>
                {needle(value, data, cx, cy, iR, oR, '#d0d000')}
            </PieChart>
        </div>
    );
};

export default Meter;

You can simply get the data attribute from the object with conData.data.
Also don't forget to add width and height attributes to your PieChart.

<PieChart width={400} height={500}>
英文:

I just changed the data type you were passing on your Meter component from Array to Object.

Calculation.js

const handleStates = (e) =&gt; {
const getStateId = e.target.value
const getAvgEnergyData = statesData.find(state =&gt; state.state_id === getStateId)?.energyData
setAvgConsumptionData(getAvgEnergyData[0])
setStatesId(getStateId)
}

Meter.js

const Meter = ({ conData}) =&gt; {
console.log(conData);
const RADIAN = Math.PI / 180;
const data = [
{ name: &#39;0&#39;, value: 0, color: &#39;#ff0000&#39; },
{ name: &#39;150&#39;, value: 150, color: &#39;#00ff00&#39; },
{ name: &#39;300&#39;, value: 300, color: &#39;#0000ff&#39; },
{ name: &#39;450&#39;, value: 450, color: &#39;#924f64&#39; },
{ name: &#39;600&#39;, value: 600, color: &#39;#dc8965&#39; },
{ name: &#39;750&#39;, value: 750, color: &#39;#d2e1f7&#39; },
{ name: &#39;900&#39;, value: 900, color: &#39;#131b27&#39; },
];
const cx = 150;
const cy = 200;
const iR = 50;
const oR = 100;
const value = conData.data
// const value = conData[0].data
// const value = conData.map(x =&gt; x.data)
const needle = (value, data, cx, cy, iR, oR, color) =&gt; {
let total = 0;
data.forEach((v) =&gt; {
total += v.value;
});
const ang = 180.0 * (1 - value / total);
const length = (iR + 2 * oR) / 3;
const sin = Math.sin(-RADIAN * ang);
const cos = Math.cos(-RADIAN * ang);
const r = 5;
const x0 = cx + 5;
const y0 = cy + 5;
const xba = x0 + r * sin;
const yba = y0 - r * cos;
const xbb = x0 - r * sin;
const ybb = y0 + r * cos;
const xp = x0 + length * cos;
const yp = y0 + length * sin;
return [
&lt;circle cx={x0} cy={y0} r={r} fill={color} stroke=&quot;none&quot; /&gt;,
&lt;path d={`M${xba} ${yba}L${xbb} ${ybb} L${xp} ${yp} L${xba} ${yba}`} stroke=&quot;#none&quot; /&gt;,
];
};
return (
&lt;div&gt;
&lt;PieChart width={400} height={500}&gt;
&lt;Pie
dataKey=&quot;value&quot;
startAngle={180}
endAngle={0}
data={data}
cx={cx}
cy={cy}
innerRadius={iR}
outerRadius={oR}
fill=&quot;#8884d8&quot;
stroke=&quot;none&quot;
&gt;
{data.map((entry, index) =&gt; (
&lt;Cell key={`cell-${index}`} fill={entry.color} /&gt;
))}
&lt;/Pie&gt;
{needle(value, data, cx, cy, iR, oR, &#39;#d0d000&#39;)}
&lt;/PieChart&gt;
&lt;/div&gt;
);
};
export default Meter;

You can simply get the data attribute from the object with conData.data.
Also don't forget to add width and height attributes to your PieChart.

&lt;PieChart width={400} height={500}&gt;

huangapple
  • 本文由 发表于 2023年5月7日 00:49:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/76190071.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定