How to pass data to a Chart.js chart in React

Viewed 2130

I'm working on a project and in this project you can type the amount of money you spent in a week(I have Groceries, Shopping, Health and Home Needs inputs), and it calculates the total amount adding it to a component. Another feature inside this project is a chart made with Chart.js and it should display the amounts of money spended with groceries, shopping, health and home needs. But I'm having trouble with this part. I'm trying to pass the data stored in the controlled inputs by props, but the data is not being displayed in the chart. Can someone help me with that ? Here's the code:

 import React from "react";

import Header from "./Header";
import InputComponent from "./InputComponent";
import AmountCard from "./AmountCard";

import BillChart from "./BillsChart";

class SpendsApp extends React.Component {
constructor(props) {
 super(props);
 this.state = {
   groceries: 10,
   shopping: 0,
   health: 0,
   houseNeeds: 0,
   total: 0,
   chartData: []
 };
 this.handleChange = this.handleChange.bind(this);
 this.handleSubmit = this.handleSubmit.bind(this);
}

handleChange(e) {
 const { name, value } = e.target;
 this.setState({
   [name]: value
 });
}

handleSubmit(e) {
 const { groceries, shopping, health, houseNeeds } = this.state;

 const gSum = parseInt(groceries, 10);
 const sSum = parseInt(shopping, 10);
 const hSum = parseInt(health, 10);
 const hnSum = parseInt(houseNeeds, 10);

 this.setState({
   total: gSum + sSum + hSum + hnSum
 });
 e.preventDefault();
}

render() {
 return (
   <div className="main">
     <Header />
     <hr />
     <div>
       <form onSubmit={this.handleSubmit}>
         <div className="input-flex">
           <InputComponent
             handleChange={this.handleChange}
             name="groceries"
             placeholder="Groceries"
           />
           <InputComponent
             handleChange={this.handleChange}
             name="shopping"
             placeholder="Shopping"
           />
           <InputComponent
             handleChange={this.handleChange}
             name="health"
             placeholder="Health"
           />
           <InputComponent
             handleChange={this.handleChange}
             name="houseNeeds"
             placeholder="Home"
           />
         </div>
         <button>Calculate</button>
       </form>
     </div>

     <AmountCard spent={this.state.total} />

     <BillChart spendings={[this.state.chartData]} />
   </div>
 );
}
}

export default SpendsApp;

here's the second one

 import React from "react";

import { Bar } from "react-chartjs-2";

class BillsChart extends React.Component {
constructor(props) {
 super(props);
 this.state = {
   chartData: {
     labels: ["Groceries", "Shopping", "Health", "House Needs"],
     datasets: [
       {
         label: "Amount Spent",
         backgroundColor: "green",
         hoverBackgroundColor: "green",
         hoverBorderWidth: 2,
         hoverBorderColor: "#000",
         data: [this.props.spendings]
       }
     ]
   }
 };
}

render() {
 return <Bar onClick={this.props.handleClick} data={this.state.chartData} />;
}
}

export default BillsChart;

3 Answers

Firstly, in your SpendsApp component you have never changed the value of the chartData state. You change the number of the total and the other properties but you never change the value of the chartData you pass.

You can have something like that

 //code....
  constructor(props) {
    super(props);

    this.state = {
      groceries: 10,
      shopping: 0,
      health: 0,
      houseNeeds: 0,
      total: 0,
      chartData: []
    }
 }



handleSubmit(e) {
   e.preventDefault();
   const { groceries, shopping, health, houseNeeds } = this.state;

   const gSum = parseInt(groceries, 10);
   const sSum = parseInt(shopping, 10);
   const hSum = parseInt(health, 10);
   const hnSum = parseInt(houseNeeds, 10);

  // chartData array elements should be ordered as in your labels in the <BillsChart/>
  // labels: ["Groceries", "Shopping", "Health", "House Needs"] as you have it
  this.setState({
      total: gSum + sSum + hSum + hnSum,
      chartData: [gSum, sSum, hSum, hnSum] 
  });
}

Also another thing is that instead of this:

<BillChart spendings={[this.state.chartData]} />

try this

<BillChart spendings={this.state.chartData} />

The reason for that is because, if you passed correctly the values in the array you will end up in with a two-dismentional array here data: [this.props.spendings] .

If you have already mounted your component you should use componentDidUpdate() to change the state of the <BillChart/>:

componentDidUpdate(prevProps) {
  if (prevProps.spendings !== this.props.spendings) {
     let chartData = this.state.chartData;

     chartData.datasets[0].data = this.props.spendings
     this.setState({chartData});
  }
}

Just write a method upside the render() in BillsChart

    componentDidUpdate(prevProps) {
    if (prevProps.spendings !== this.props.spendings) {

     let {chartData} = this.state;
     chartData.datasets[0].data = this.props.spendings
     this.setState({chartData});

    }
  }

You are setting the prop spending on your BillChart in the render, which is passing an empty array as a prop to the component.

this.state = {
   groceries: 10,
   shopping: 0,
   health: 0,
   houseNeeds: 0,
   total: 0,
   chartData: []
 };


 <BillChart spendings={[this.state.chartData]} />

So when you setState on chartData in your BillsChart component it is setting the data property to that empty array.

this.state = {
   chartData: {
     labels: ["Groceries", "Shopping", "Health", "House Needs"],
     datasets: [
       {
         label: "Amount Spent",
         backgroundColor: "green",
         hoverBackgroundColor: "green",
         hoverBorderWidth: 2,
         hoverBorderColor: "#000",
         data: [this.props.spendings]
       }
     ]
   }
 };

You need to setState of chartData to something besides an empty array before you pass it as a prop.

Related