简体   繁体   中英

Pushing API Data into Chart.js

I'm trying to push my API data into my chart, but there seems to be a problem with the timing of when the data is updated versus when the chart is rendered. I'm console.logging my two arrays, and it isn't showing anything in the array. Does anyone know what I need to change in order for my data to be updated to the state, before the chart renders with that data?

function SmallBox(props) {
    const [chartNums, setChartNums] = useState([])
    const [chartLabels, setChartLabels] = useState([])
    const [chartData, setChartData] = useState({})
    const x = [];
    const y = [];

    const fetchData = async () => {
      await fetch(`https://api.coingecko.com/api/v3/coins/${props.id}/market_chart?vs_currency=usd&days=1`)
      .then((response) => {
        return response.json();
      })
      .then((data) => {
        for (let i = 0; i < data.prices.length; i++){
          x.push(data.prices[i][0])
          setChartLabels(x)
        }

        for (let i = 0; i < data.prices.length; i++){
          y.push(data.prices[i][1])
          setChartNums(y)
        }
        
      })
    };

    const chart = async () => {
        await fetchData()
        console.log(chartNums)
        console.log(chartLabels)
        
        setChartData({
            labels: chartLabels,
            datasets: [
                {
                    label: '$',
                    data: chartNums,
                    backgroundColor: ['rgba(0,0,0,0.09)'],
                    borderColor: `${props.color}`,
                    borderWidth: 4,
                    borderJoinStyle: 'round',
                    borderCapStyle: 'round',
                    borderWidth: 3,
                    pointRadius: 0,
                    pointHitRadius: 10,
                    lineTension: .2,
                }
            ]
        })
    }

    useEffect(() =>{
      chart();
    }, []);

    return (
        <div id={props.id} className="smallBox">
            <div className="smallBox_info">
                <img className="smallBox-icon" src={props.image} alt={props.symbol}/>
                <h2>{props.title}</h2>
            </div>
            
            <div className="smallBox_numbers">
                <h2 className="smallBox-price">$ {props.currentPrice}</h2>
                <h5 className="smallBox-roc">{props.percentChange}</h5>
            </div>
            
            <div className="smallBox_graph">
                <Line data={chartData} options={{
                    responsive: true,
                    maintainAspectRatio: false,
                    title: {text: 'ThickBoyz', display: false},
                    legend: {display: false},
                    layout: {
                        padding: {
                          left: 0,
                          right: 0,
                          top: 0,
                          bottom: 0
                        }
                      },
                      scales: {
                        xAxes: [{
                          display: false,
                          gridLines: {}
                        }],
                        yAxes: [{
                          display: false,
                          gridLines: {}
                        }]
                      },
                      tooltips: {
                        callbacks: {
                          //This removes the tooltip title
                          title: function() {}
                       },
                        //this removes legend color
                        displayColors: false,
                        yPadding: 10,
                        xPadding: 10,
                        position: 'nearest',
                        caretSize: 10,
                        backgroundColor: 'rgba(255,255,255,.9)',
                        bodyFontSize: 15,
                        bodyFontColor: '#303030' 
                      }
                    }}/>  
            </div>
        </div>
    )
}

export default SmallBox

use async- await properly, don't mix promise then method with await.

import React, { useEffect, useState, useCallback } from "react";
import { Line } from "react-chartjs-2";
import "./styles.css";

export default function App() {
  const [chartNums, setChartNums] = useState([]);
  const [chartLabels, setChartLabels] = useState([]);
  const [chartData, setChartData] = useState({});
  const x = [];
  const y = [];
  const props = {
    id: "bitcoin"
  };

  const fetchData = useCallback(async () => {
    const response = await fetch(
      `https://api.coingecko.com/api/v3/coins/${props.id}/market_chart?vs_currency=usd&days=1`
    );
    const data = await response.json();
    if (data && data.prices) {
      console.log(data.prices);
      for (let i = 0; i < data.prices.length; i++) {
        x.push(data.prices[i][0]);
        setChartLabels(x);
      }

      for (let i = 0; i < data.prices.length; i++) {
        y.push(data.prices[i][1]);
        setChartNums(y);
      }
    }
  }, [props]);

  const chart = useCallback(async () => {
    await fetchData();
    console.log(chartNums);
    console.log(chartLabels);

    setChartData({
      labels: chartLabels,
      datasets: [
        {
          label: "$",
          data: chartNums,
          backgroundColor: ["rgba(0,0,0,0.09)"],
          borderColor: `${props.color}`,
          borderWidth: 4,
          borderJoinStyle: "round",
          borderCapStyle: "round",
          pointRadius: 0,
          pointHitRadius: 10,
          lineTension: 0.2
        }
      ]
    });
  }, [chartNums, chartLabels, fetchData, props.color]);

  useEffect(() => {
    chart();
  }, [chart]);

  return (
    <div id={props.id} className="smallBox">
      <div className="smallBox_info">
        <img className="smallBox-icon" src={props.image} alt={props.symbol} />
        <h2>{props.title}</h2>
      </div>

      <div className="smallBox_numbers">
        <h2 className="smallBox-price">$ {props.currentPrice}</h2>
        <h5 className="smallBox-roc">{props.percentChange}</h5>
      </div>

      <div className="smallBox_graph">
        <Line
          data={chartData}
          options={{
            responsive: true,
            maintainAspectRatio: false,
            title: { text: "ThickBoyz", display: false },
            legend: { display: false },
            layout: {
              padding: {
                left: 0,
                right: 0,
                top: 0,
                bottom: 0
              }
            },
            scales: {
              xAxes: [
                {
                  display: false,
                  gridLines: {}
                }
              ],
              yAxes: [
                {
                  display: false,
                  gridLines: {}
                }
              ]
            },
            tooltips: {
              callbacks: {
                //This removes the tooltip title
                title: function () {}
              },
              //this removes legend color
              displayColors: false,
              yPadding: 10,
              xPadding: 10,
              position: "nearest",
              caretSize: 10,
              backgroundColor: "rgba(255,255,255,.9)",
              bodyFontSize: 15,
              bodyFontColor: "#303030"
            }
          }}
        />
      </div>
    </div>
  );
}

Sandbox link - https://codesandbox.io/s/chartjs-fetchapidata-r3ghc?file=/src/App.js

Consider sticking to either .then() syntax or async/await . I would recommend using the latter, which should make things more straightforward:

 const fetchData = async () => {
      const response = await fetch(`https://api.coingecko.com/api/v3/coins/1/market_chart?vs_currency=usd&days=1`)
      const data = await response.json()
      data.prices.forEach(price => {
        x.push(price[0])
        setChartLabels(x)
        
        y.push(price[1])
        setChartNums(y)
    });
}

Next, consider whether you actually need to store chartLabels and chartNums in React state - they aren't used in your JSX, so returning them as plain old variables is probably clearer:

 const fetchData = async () => {
      const response = await fetch(`https://api.coingecko.com/api/v3/coins/1/market_chart?vs_currency=usd&days=1`)
      const data = await response.json()
      chartLabels = data.prices.map(price => price[0])
      chartNums = data.prices.map(price => price[1]
      return [chartLabels,chartNums]
}

then later,

const [chartLabels, chartNums] = await fetchData()
  const [chartLabels, setChartLabels] = useState([]);
  const [chartData, setChartData] = useState({});
  const x = [];
  const y = [];
  const props = {
    id: "bitcoin"
  };

  const fetchData = useCallback(async () => {
    const response = await fetch(
      `https://api.coingecko.com/api/v3/coins/${props.id}/market_chart?vs_currency=usd&days=1`
    );
    const data = await response.json();
    if (data && data.prices) {
      console.log(data.prices);
      for (let i = 0; i < data.prices.length; i++) {
        x.push(data.prices[i][0]);
        setChartLabels(x);
      }

      for (let i = 0; i < data.prices.length; i++) {
        y.push(data.prices[i][1]);
        setChartNums(y);
      }
    }
  }, [props,x,y]);

Referring to the solution provided above you can init the x and y arrays wrapping useMemo Hook to return and more optimal solution and prevent Warnings like this

Pro tip: if you add the dependencies incorrectly or none at all repetitive API calls might get you block.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM