Search code examples
javascriptreactjschart.jsreact-chartjs

react-chartjs state update error


I made a React.JS component, which draws charts using react-chartjs wrapper around the chart.js library. I am getting an error on update, as if my data was corrupted. I am thinking that maybe something is wrong with the context, maybe, as I don't see anything wrong with my code. When the chart is drawn first time, it shows up fine. setState causes: Uncaught Type Error: Cannot read property 'points' of undefined(...) happens at core.js anonymous function inside set.data.forEach(). Below is my code:

class Usage extends Component {

  constructor(props) {
    super(props);
    this.state = {
      active: true,
      new: false,
      chartData: []
    }
  }

  componentWillMount() {
    this.buildChartData();
  }

buildChartData() {
    const daysInMonth = 30;
    const graphs = [
      {title: 'Active Users', key: 'active', color: 'rgba(255, 99, 132, 1)'}, 
      {title: 'New Users', key: 'new', color: 'rgba(50, 25, 255, 1)'}
    ];
    let datasets = [];
    let labels = [];
    let labelsDone = false;

    graphs.forEach((graph) => {

      if (this.state[graph.key]) {
        let data = [];
        let backgroundColor = [];
        let borderColor = [];

        // XXX !!! for now
        if (!labelsDone) {
          for (let i = daysInMonth; i > 0; i--) {
            let d = new Date();
            // we stop yesterday
            d.setDate(d.getDate() - i);
            labels.push( (d.getUTCMonth() + 1) + '/' + d.getUTCDate() );
          }
        }
        labelsDone = true;

        for (let i = daysInMonth; i > 0; i--) {
          let d = new Date();
          // we stop yesterday
          d.setDate(d.getDate() - i);
          data.push( Math.round(Math.random() * 200));
          borderColor.push(graph.color);
          backgroundColor.push('rgba(0,0,0,0)');
        }
        let dataset = {
          data: data, 
          borderWidth: 1, 
          label: graph.title,
          fillColor: 'rgba(0,0,0,0.0)',
          strokeColor: borderColor
        };
        datasets.push(dataset);
      }
    })
    this.setState({
      chartData: {datasets: datasets, labels: labels}
    });
  }

  toggleCheckbox = label => {
    switch(label) {
      case 'Active Users':
        this.setState({
          active: !this.state.active
        });
        break;
      case 'New Users':
        this.setState({
          new: !this.state.new
        });
        break;
      default:
        console.log('Error: unhandled action');
        break;
    }
    this.buildChartData();
  }

  render() {
    return (
        <div style={{display: 'flex', flexDirection: 'row'}}>
          <div style={{flexDirection: 'column', marginLeft: '50px', marginRight: '50px', flexGrow: 1}}>
            <div style={{height: '20vh'}}>
              <div style={{flexDirection: 'row'}}>
                <Checkbox
                  label={'Active Users'}
                  handleCheckboxChange={this.toggleCheckbox}
                  isChecked={true}
                />
                <Checkbox
                  label={'New Users'}
                  handleCheckboxChange={this.toggleCheckbox}
                  isChecked={false}
                />
              </div>
            </div>
            <div style={{height: '75vh'}}>
              <Line data={this.state.chartData} options={chartOptions} />
            </div>
          </div>
        </div>
      );
  }
}

export default Usage;

I printed out smaller dataset, where the same thing happens. Don't see any difference except for randomly generated values. Still, the initial rendering is fine, yet setState, which I understand calls update results in the above error.


Solution

  • If data passed into the component changes, points will animate between values using chart.js' .update(). If you want the chart destroyed and redrawn on every change, pass in redraw as a prop. For example <LineChart data={this.state.chartData} redraw />

    Adding redraw solves the problem.