2

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 ?

2 Answers 2

1

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}
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks @Khabir but this did not solve all my problem but a part of it definitely.
You are welcome, I tried a lot to show the graph but was unable. you provided an code link on other answer where it includes conditional statement like this.state.countries.length > 0 but this.state.countries is not an array, it is an object. you must remove that condition. Please check this code link: codesandbox.io/s/unruffled-kepler-ecq0d
Thanks for trying @Khabir.I have got this output at some point but again edited it to get expected output. If you get any idea how this graph can be plotted please ping here
0

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.

7 Comments

did this work and output was as given in gif ? @Florian
I'm getting a blank screen after loading
I let you test my suggestion and see if it suits your needs. I did not setup your use case.
You can setup a code sandbox, it would be simplier to help (codesandbox.io)
can you test it from your end if possible
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.