reactjsnext.jsrecharts

Data structure changes when fetching from Flask backend with react API


I have a flask API for a backend and a react/nextjs frontend. I'm trying to build a simple bar chart based on some data fetched from the backend but am having some trouble fetching and mapping the data correctly.

When I hardcode some data in json format the plot works. But when I try to use data fetched from the API I get errors. It looks like it might have to do with type, when hardcoded the console.log output lists the Prototype as array but when I pull from the API it's listed as Object.

I tried converting the object to an array but when I do so I lose the keys and only keep the values. I'd like to pull the data from my API and keep it in the same structure of the hardcoded data so it can be plotted but cannot figure out how to properly map it to do so.

API Data:

{
  "grc_external": 32,
  "name": "Storage",
  "open": 293,
  "sequence": 175
}

Code:

import React, { useEffect, useState } from "react";
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Legend, Tooltip, ResponsiveContainer } from 'recharts';
import dynamic from "next/dynamic";

function storage() {
    
    /* hard coding this data works
    const [storageData, setStorageData] = useState([
        { name: 'Storage', sequence: 100, grc_external: 100, open: 300 },
    ]);
    */

    /* fetching the data does not */
    const [storageData, setStorageData] = useState([]);
    const fetchStorageData = () => {
        fetch("http://localhost:8080/api/storage")
        .then(response => {
            return response.json()
        })
        .then(data => {
            setStorageData(data)
        })
    }
    useEffect(() => {
        fetchStorageData()
    }, []);
    
    console.log(storageData)
    const storageData2 = Object.entries(storageData)
    console.log(storageData2)

    const BarChart = dynamic(() => (
        import("recharts").then(recharts => recharts.BarChart)
    ), { ssr: false });

    return (
    <ResponsiveContainer className="data_storage_chart" width="80%" height={200} >
        <BarChart data={storageData2} layout="vertical" barCategoryGap={1}>
            <CartesianGrid />
            <XAxis type="number" domain={[0, 500]} ticks={[0, 50, 100, 150, 200, 250, 300, 350, 400, 450, 500]} />
            <YAxis type="category" dataKey="name" tick={false} />
            <Tooltip />
            <Legend />
            <Bar dataKey="sequence" stackId="a" fill="#ab0000" />
            <Bar dataKey="grc_external" stackId="a" fill="#5aaafa" />
            <Bar dataKey="open" stackId="a" fill="#b0b0b0" />
        </BarChart>
    </ResponsiveContainer>
    );
}
  
export default storage;

Flask Endpoint:

@app.route("/api/storage", methods=['GET'])
def storage():
    data = jsonify({
        'name': 'Storage', 
        'sequence': 175, 
        'grc_external': 32, 
        'open': 293
    })
    return data

console.log(storageData) without API: enter image description here

console.log(storageData) with API: enter image description here


Solution

  • In your hard coded storageData, you have an array around it:

    useState([ <-----
            { name: 'Storage', sequence: 100, grc_external: 100, open: 300 },
    ] <----
    );
    

    Which is also reflected by the picture showing how the data is the 0th index of an array in the hard-coded version. Flask however is just returning the object, not as a member of an array. You either need to add an array to the data being stringified in python, or remove the need for it to be initially wrapped in an array and then take the hard-coded version out of an array. The latter seems like the better choice.

    Also worth noting, your "Object.entries" call doesn't do anything to an array, which is why it doesn't affect the hard-coded version (as it is an array), but messes up the API call.