Reputation: 1663
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
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
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