reactjsmaterial-uifetch-apipie-chart

N.map is not a function keep on showing


I want to generate a Pie Chart, but I keep on having this error even if my array is containing all the necessary information to be displayed on the Pie Chart (via the console log). I don't even know where this error come from on my code:

enter image description here enter image description here

here is my Pie component :

    import { Box } from "@mui/material";
    import Header from "../components/Header";
    import PieChart from "../components/PieChart";
    import { useEffect, useState } from "react";
    import { mockDistrictData } from "../data/MockData";

    const Pie = () => {
        const [districts, setDistricts] = useState([]);
        const [selectedDistrict, setSelectedDistrict] = useState('');
        const [finalData, setFinalData] = useState([]);
        const lightGreenColor = "hsl(154,52%,45%)";
        const greenColor = "hsl(139,100%,83%)";
        const [isGeneratingChart, setIsGeneratingChart] = useState(false);

        // Fetch data for mechanical bikes
        const fetchMechanicalBikes = (districtName) => {
            const url = `http://localhost:8800/station_status/bikes/mechanical/${districtName}`;
            return fetch(url)
                .then(response => response.json())
                .then(data => {
                    // Transform the fetched data and assign random colors
                    return data.map(i => ({
                        id: `Mechanical bikes`,
                        label: `Mechanical bikes`,
                        value: i.value,
                        color: lightGreenColor
                    }));
                });
        };

        // Fetch data for electronic bikes
        const fetchElectronicBikes = (districtName) => {
            const url = `http://localhost:8800/station_status/bikes/electric/${districtName}`;
            return fetch(url)
                .then(response => response.json())
                .then(data => {
                    // Transform the fetched data and assign random colors
                    return data.map(item => ({
                        id: `Electronic bikes`,
                        label: `Electronic bikes`,
                        value: item.value,
                        color: greenColor
                    }));
                });
        };

        // Combine the fetched data for mechanical and electronic bikes
        const combineFetchedData = async (districtName) => {
            try {
                setIsGeneratingChart(true); // Set the flag to indicate chart generation in progress
                const mechanicalData = await fetchMechanicalBikes(districtName);
                const electronicData = await fetchElectronicBikes(districtName);

                // Combine the two arrays into a single array
                const combinedData = [...mechanicalData, ...electronicData];

                setTimeout(() => {
                    setFinalData(combinedData);
                    console.log(combinedData); // Print the combined data
                    setIsGeneratingChart(false); // Set the flag to indicate chart generation completed
                }, 3000); // 3 seconds delay

                // You can use the combined data for further processing or visualization

            } catch (error) {
                console.error('Error fetching data: ', error);
                setIsGeneratingChart(false); // Set the flag to indicate chart generation completed with an error
            }
        };

        useEffect(() => {
            // Fetch districts
            const fetchDistricts = () => {
                setDistricts(mockDistrictData.map(district => district.district_name));
            };

            fetchDistricts();
        }, []);

        return (
            <Box m="20px">
                <Header title="Pie Chart" subtitle="Electric Bikes & Mechanical Bikes" />
                <Box height="75vh">
                    <div>
                        <select value={selectedDistrict} onChange={e => setSelectedDistrict(e.target.value)}>
                            <option value="">Select a district</option>
                            {districts.map(district => (
                                <option key={district} value={district}>{district}</option>
                            ))}
                        </select>
                        <button disabled={isGeneratingChart} onClick={() => combineFetchedData(selectedDistrict)}>
                            {isGeneratingChart ? 'Generating...' : 'Generate Chart'}
                        </button>
                    </div>
                    {finalData.length > 0 && <PieChart data={finalData} />}
                </Box>
            </Box>
        );
    };

    export default Pie;

here is my PieChart component :

    import { ResponsivePie } from "@nivo/pie";
    import { tokens } from "../theme";
    import { useTheme } from "@mui/material";
    const PieChart = (finalData) => {
        const theme = useTheme();
        const colors = tokens(theme.palette.mode);

        return (
            <ResponsivePie
                data={finalData}
                theme={{
                    axis: {
                        domain: {
                            line: {
                                stroke: colors.grey[100],
                            },
                        },
                        legend: {
                            text: {
                                fill: colors.grey[100],
                            },
                        },
                        ticks: {
                            line: {
                                stroke: colors.grey[100],
                                strokeWidth: 1,
                            },
                            text: {
                                fill: colors.grey[100],
                            },
                        },
                    },
                    legends: {
                        text: {
                            fill: colors.grey[100],
                        },
                    },
                }}
                margin={{ top: 40, right: 80, bottom: 80, left: 80 }}
                innerRadius={0.5}
                padAngle={0.7}
                cornerRadius={3}
                activeOuterRadiusOffset={8}
                borderColor={{
                    from: "color",
                    modifiers: [["darker", 0.2]],
                }}
                arcLinkLabelsSkipAngle={10}
                arcLinkLabelsTextColor={colors.grey[100]}
                arcLinkLabelsThickness={2}
                arcLinkLabelsColor={{ from: "color" }}
                enableArcLabels={false}
                arcLabelsRadiusOffset={0.4}
                arcLabelsSkipAngle={7}
                arcLabelsTextColor={{
                    from: "color",
                    modifiers: [["darker", 2]],
                }}
                defs={[
                    {
                        id: "dots",
                        type: "patternDots",
                        background: "inherit",
                        color: "rgba(255, 255, 255, 0.3)",
                        size: 4,
                        padding: 1,
                        stagger: true,
                    },
                    {
                        id: "lines",
                        type: "patternLines",
                        background: "inherit",
                        color: "rgba(255, 255, 255, 0.3)",
                        rotation: -45,
                        lineWidth: 6,
                        spacing: 10,
                    },
                ]}
                legends={[
                    {
                        anchor: "bottom",
                        direction: "row",
                        justify: false,
                        translateX: 0,
                        translateY: 56,
                        itemsSpacing: 0,
                        itemWidth: 100,
                        itemHeight: 18,
                        itemTextColor: "#999",
                        itemDirection: "left-to-right",
                        itemOpacity: 1,
                        symbolSize: 18,
                        symbolShape: "circle",
                        effects: [
                            {
                                on: "hover",
                                style: {
                                    itemTextColor: "#000",
                                },
                            },
                        ],
                    },
                ]}
            />
        );
    };

    export default PieChart;

Can somebody help me solve the problem please ?


Solution

  • Your PieChart component receives props, not a finalData argument.

    <PieChart data={finalData} />
    
    // finalData here is a props object that has a data field
    const PieChart = (finalData) => {
     ...
     <ResponsivePie
        data={finalData} // finalData isn't what you appear to think it is.
    

    Suggested fix:

    const PieChart = (props) => {
     ...
    <ResponsivePie
      data={props.data}
      ...
    

    Or you could destructure data from props if you prefer:

    const PieChart = ({ data }) => {
     ...
    <ResponsivePie
      data={data}
      ...
    

    ResponsivePie expects data to be an array and is attempting to call map on it. (I assume.)