raiyan khan
raiyan khan

Reputation: 3

How to show dynamic data in a pie chart with needle

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

Answers (1)

Cacci
Cacci

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

Related Questions