diff --git a/CHANGELOG.md b/CHANGELOG.md
index bcc8bb945..0b78cef33 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
+## UNRELEASED
+### Fixed
+- Fixed problems with `Graph` components leaking events and being recreated multiple times if declared with no ID [#604](https://github.com/plotly/dash-core-components/pull/604)
+
## [1.1.1] - 2019-08-06
- Upgraded plotly.js to 1.49.1 [#595](https://github.com/plotly/dash-core-components/issues/595)
- Patch release [1.49.1](https://github.com/plotly/plotly.js/releases/tag/v1.49.1)
diff --git a/src/components/Graph.react.js b/src/components/Graph.react.js
index b4bce1e4c..4d467dd3a 100644
--- a/src/components/Graph.react.js
+++ b/src/components/Graph.react.js
@@ -64,67 +64,66 @@ const filterEventData = (gd, eventData, event) => {
return filteredEventData;
};
-function generateId() {
- const charAmount = 36;
- const length = 7;
- return (
- 'graph-' +
- Math.random()
- .toString(charAmount)
- .substring(2, length)
- );
-}
-
/**
* Graph can be used to render any plotly.js-powered data visualization.
*
* You can define callbacks based on user interaction with Graphs such as
* hovering, clicking or selecting
*/
-const GraphWithDefaults = props => {
- const id = props.id ? props.id : generateId();
- return ;
-};
-
class PlotlyGraph extends Component {
constructor(props) {
super(props);
+ this.gd = React.createRef();
this.bindEvents = this.bindEvents.bind(this);
this._hasPlotted = false;
+ this._prevGd = null;
this.graphResize = this.graphResize.bind(this);
}
plot(props) {
- const {figure, id, animate, animation_options, config} = props;
- const gd = document.getElementById(id);
+ const {figure, animate, animation_options, config} = props;
+ const gd = this.gd.current;
if (
animate &&
this._hasPlotted &&
figure.data.length === gd.data.length
) {
- return Plotly.animate(id, figure, animation_options);
+ return Plotly.animate(gd, figure, animation_options);
}
- return Plotly.react(id, {
+ return Plotly.react(gd, {
data: figure.data,
layout: clone(figure.layout),
frames: figure.frames,
config: config,
}).then(() => {
- if (!this._hasPlotted) {
- // double-check gd hasn't been unmounted
- const gd = document.getElementById(id);
- if (gd) {
- this.bindEvents();
- Plotly.Plots.resize(gd);
- this._hasPlotted = true;
+ const gd = this.gd.current;
+
+ // double-check gd hasn't been unmounted
+ if (!gd) {
+ return;
+ }
+
+ // in case we've made a new DOM element, transfer events
+ if(this._hasPlotted && gd !== this._prevGd) {
+ if(this._prevGd && this._prevGd.removeAllListeners) {
+ this._prevGd.removeAllListeners();
+ Plotly.purge(this._prevGd);
}
+ this._hasPlotted = false;
+ }
+
+ if (!this._hasPlotted) {
+ this.bindEvents();
+ Plotly.Plots.resize(gd);
+ this._hasPlotted = true;
+ this._prevGd = gd;
}
});
}
extend(props) {
- const {id, extendData} = props;
+ const {extendData} = props;
let updateData, traceIndices, maxPoints;
if (Array.isArray(extendData) && typeof extendData[0] === 'object') {
[updateData, traceIndices, maxPoints] = extendData;
@@ -143,20 +142,21 @@ class PlotlyGraph extends Component {
traceIndices = generateIndices(updateData);
}
- return Plotly.extendTraces(id, updateData, traceIndices, maxPoints);
+ const gd = this.gd.current;
+ return Plotly.extendTraces(gd, updateData, traceIndices, maxPoints);
}
graphResize() {
- const graphDiv = document.getElementById(this.props.id);
- if (graphDiv) {
- Plotly.Plots.resize(graphDiv);
+ const gd = this.gd.current;
+ if (gd) {
+ Plotly.Plots.resize(gd);
}
}
bindEvents() {
- const {id, setProps, clear_on_unhover} = this.props;
+ const {setProps, clear_on_unhover} = this.props;
- const gd = document.getElementById(id);
+ const gd = this.gd.current;
gd.on('plotly_click', eventData => {
const clickData = filterEventData(gd, eventData, 'click');
@@ -212,8 +212,10 @@ class PlotlyGraph extends Component {
}
componentWillUnmount() {
- if (this.eventEmitter) {
- this.eventEmitter.removeAllListeners();
+ const gd = this.gd.current;
+ if (gd && gd.removeAllListeners) {
+ gd.removeAllListeners();
+ Plotly.purge(gd);
}
window.removeEventListener('resize', this.graphResize);
}
@@ -262,6 +264,7 @@ class PlotlyGraph extends Component {