Reputation: 3
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?
Upvotes: 0
Views: 1040
Reputation: 381
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}>
Upvotes: 0