fnsjdnfksjdb
fnsjdnfksjdb

Reputation: 1663

Deleting and recreating an element with React

I'm trying to get Chart.js to work with React.js and I'm close.

I have React rendering the correct graph, and when I change state the new chart is drawn overtop of the other one. The issue is that the hover events are clearly being left on the canvas, and moving the mouse over it flashes all versions of the graph that have been made before.

This is a bug in Chart.js as far as I know, and the solution is to delete the canvas element and replace it with a new canvas element.

So I have this code, which gets the graph on the page:

var LineChart = React.createClass({
  render: function() {
    return (
      <div id="lineHolder" className="graphHolder">
        <h2 className="sectionHeader">Earnings per day</h2>
        <canvas id="lineChart" width="688" height="286"></canvas>
      </div>
    );
  },
  ctx: {},
  lineChart: {},
  componentDidMount: function() {
    this.drawChart();
  },
  componentDidUpdate: function() {
    this.drawChart();
  },
  drawChart: function() {
    var lineData = {
      labels: this.props.dates,
      datasets: [{
        label: "Earnings",
        fillColor: "rgba(151,187,205,0.2)",
        strokeColor: "rgba(151,187,205,1)",
        pointColor: "rgba(151,187,205,1)",
        pointStrokeColor: "#fff",
        pointHighlightFill: "#fff",
        pointHighlightStroke: "rgba(151,187,205,1)",
        data: this.props.earnings,
      }]
    }
    this.ctx = document.getElementById("lineChart").getContext("2d");
    this.lineChart = new Chart(this.ctx).Line(lineData);
  }
});

But I can't think of the "react way" to delete and replace that canvas element, every time it needs to be redraw.

Any ideas?

Upvotes: 3

Views: 2255

Answers (2)

hazardous
hazardous

Reputation: 10837

Try this -

var LineChart = React.createClass({
  render: function() {
    return (
      <div id="lineHolder" className="graphHolder">
        <h2 className="sectionHeader">Earnings per day</h2>
        <canvas ref="lineChart" key={this.state.canvasUpdateToggle} width="688" height="286"></canvas>
      </div>
    );
  },
  ctx: {},
  lineChart: {},
  getInitialState:function(){
    return {canvasUpdateToggle:"0"};
  },
  componentDidMount: function() {
    this.drawChart();
  },
  componentDidUpdate: function() {
    this.drawChart();
  },
  componentWillReceiveProps:function(){
    var canvasUpdateToggle = this.state.canvasUpdateToggle == 0? 1: 0;
    this.setState({"canvasUpdateToggle":canvasUpdateToggle});
  },
  drawChart: function() {
    var lineData = {
      labels: this.props.dates,
      datasets: [{
        label: "Earnings",
        fillColor: "rgba(151,187,205,0.2)",
        strokeColor: "rgba(151,187,205,1)",
        pointColor: "rgba(151,187,205,1)",
        pointStrokeColor: "#fff",
        pointHighlightFill: "#fff",
        pointHighlightStroke: "rgba(151,187,205,1)",
        data: this.props.earnings,
      }]
    }
    this.ctx = this.refs.lineChart.getContext("2d");
    this.lineChart = new Chart(this.ctx).Line(lineData);
  }
});

Upvotes: 0

David L. Walsh
David L. Walsh

Reputation: 24817

In React, you want to change the view every time the props or state change. If you're using a DOM manipulation library, such as Chart.js, this is where the React lifecycle methods come in handy.

https://facebook.github.io/react/docs/component-specs.html

You can use componentWillUpdate or componentDidUpdate (depending on what suits) to compare the next state to the previous state.

componentWillUpdate(nextProps, nextState) {
    /* Optionally include logic comparing nextProps or nextState
     * to this.props or this.state
     */
    this.lineChart.clear();
}

Just make sure that it's props/state changes that drive the changes in the Chart.

I don't know much about this apparent Chart.js bug. But if you do need to re-render the canvas element each time, I would suggest placing a key attribute on the canvas element.

<canvas key={this.uniqueKey()} id="lineChart" width="688" height="286"></canvas>

Quite what this.uniqueKey() produces hard to say. But it needs to be different every time the props or state change. And only resort this if simpler methods like Chart#clear() (or possibly Chart#destroy()) fail.

Upvotes: 2

Related Questions