kelte
kelte

Reputation: 27

Plot bar graph in reactjs

I am learning reactjs and I'm trying to plot bar graph. So I'm trying to plot population race graph which change over the time and looks like this. Population race bar chart

For plotting graph I am using chart-race-react. But it seems like I'm doing something wrong in my code.

My code:

import React, { Component } from 'react';
import BarChart from 'chart-race-react';

import './App.css';
const API_URL = 'https://example.json';

const randomColor = () => {
  return `rgb(${255 * Math.random()}, ${255 * Math.random()}, ${255})`;
}

class App extends Component {
 constructor(props) {
     super(props);
     this.state = {
     countries: []
   }
 }

componentDidMount() {

fetch(API_URL)
.then(v => v.json())
.then(array => {
  const dataForVisualisation = {};

  array.forEach(element => {
      const countryName = element['Country Name'];
      if (!dataForVisualisation[countryName]) {
         dataForVisualisation[countryName] = [];
      }
      dataForVisualisation[countryName].push(element.Value);
  });

  this.setState({countries: dataForVisualisation});
})

}

render() {

 return (
    <div style={{width: "500px"}}>

     <BarChart 
      start={true}
      data = {this.state.countries}
      len = {this.state.countries[Object.keys(this.state.countries)[0]].length}
      keys = {Object.keys(this.state.countries)}
      timeout={400}
      delay={100}
      colors = {Object.keys(this.state.countries).reduce((res, item) => ({ 
        ...res, 
        ...{ [item]: randomColor() } 
    }), {})}
      labels = {Object.keys(this.state.countries).reduce((res, item, idx) => {
        return{
        ...res, 
        ...{[item]: (
          <div style={{textAlign:"center",}}>
            <div>{item}</div>
          </div>
          )}
      }}, {})}
      timeline = {Array(20).fill(0).map((itm, idx) => idx + 1)}
      timelineStyle={{
        textAlign: "center",
        fontSize: "50px",
        color: "rgb(148, 148, 148)",
        marginBottom: "100px"
      }}
      textBoxStyle={{
        textAlign: "right",
        color: "rgb(133, 131, 131)",
        fontSize: "30px",
      }}
      barStyle={{
        height: "60px",
        marginTop: "10px",
        borderRadius: "10px",
      }}
      width={[15, 75, 10]}

      maxItems = {this.state.countries.length}

    />

  </div>
    );
  }
}
export default App;

I'm encountering TypeError: this.state.countries[Object.keys(...)[0]] is undefined error because my data is undefined here. How should I change my code to make my output as shown in gif? Is there any other library for doing this ?

Upvotes: 2

Views: 415

Answers (2)

Khabir
Khabir

Reputation: 5862

I found an issue on your code like in len property of the BarChart that gets the error Cannot read property 'length' of undefined. After I used null checking with question mark, I am not getting any error but printing 1 to 20. I am not sure it helps you or not. May be after changing that, you will get it solved.

len={this.state.countries[Object.keys(this.state.countries)[0]].length}

replaced by

len={this.state.countries[Object.keys(this.state.countries)[0]]?.length}

Upvotes: 1

Florian Motteau
Florian Motteau

Reputation: 3724

You should just wait until countries is filled before trying to render anything. You can for example render BarChart component only if countries array has at least one element :

[...]
{this.state.countries.length > 0 && <BarChart 
  start={true}
  data = {this.state.countries}
  len = {this.state.countries[Object.keys(this.state.countries)[0]].length}
  keys = {Object.keys(this.state.countries)}
  timeout={400}
  delay={100}
  colors = {Object.keys(this.state.countries).reduce((res, item) => ({ 
    ...res, 
    ...{ [item]: randomColor() } 
}), {})}
  labels = {Object.keys(this.state.countries).reduce((res, item, idx) => {
    return{
    ...res, 
    ...{[item]: (
      <div style={{textAlign:"center",}}>
        <div>{item}</div>
      </div>
      )}
  }}, {})}
  timeline = {Array(20).fill(0).map((itm, idx) => idx + 1)}
  timelineStyle={{
    textAlign: "center",
    fontSize: "50px",
    color: "rgb(148, 148, 148)",
    marginBottom: "100px"
  }}
  textBoxStyle={{
    textAlign: "right",
    color: "rgb(133, 131, 131)",
    fontSize: "30px",
  }}
  barStyle={{
    height: "60px",
    marginTop: "10px",
    borderRadius: "10px",
  }}
  width={[15, 75, 10]}

  maxItems = {this.state.countries.length}

/>}

{this.state.countries.length === 0 && <span>Loading...</span>}
[...]

Note the conditional rendering syntax.

Upvotes: 0

Related Questions