@@ -64,67 +64,63 @@ const filterEventData = (gd, eventData, event) => {
64
64
return filteredEventData ;
65
65
} ;
66
66
67
- function generateId ( ) {
68
- const charAmount = 36 ;
69
- const length = 7 ;
70
- return (
71
- 'graph-' +
72
- Math . random ( )
73
- . toString ( charAmount )
74
- . substring ( 2 , length )
75
- ) ;
76
- }
77
-
78
67
/**
79
68
* Graph can be used to render any plotly.js-powered data visualization.
80
69
*
81
70
* You can define callbacks based on user interaction with Graphs such as
82
71
* hovering, clicking or selecting
83
72
*/
84
- const GraphWithDefaults = props => {
85
- const id = props . id ? props . id : generateId ( ) ;
86
- return < PlotlyGraph { ...props } id = { id } /> ;
87
- } ;
88
-
89
73
class PlotlyGraph extends Component {
90
74
constructor ( props ) {
91
75
super ( props ) ;
76
+ this . gd = React . createRef ( ) ;
92
77
this . bindEvents = this . bindEvents . bind ( this ) ;
93
78
this . _hasPlotted = false ;
79
+ this . _prevGd = null ;
94
80
this . graphResize = this . graphResize . bind ( this ) ;
95
81
}
96
82
97
83
plot ( props ) {
98
- const { figure, id , animate, animation_options, config} = props ;
99
- const gd = document . getElementById ( id ) ;
84
+ const { figure, animate, animation_options, config} = props ;
85
+ const gd = this . gd . current ;
100
86
101
87
if (
102
88
animate &&
103
89
this . _hasPlotted &&
104
90
figure . data . length === gd . data . length
105
91
) {
106
- return Plotly . animate ( id , figure , animation_options ) ;
92
+ return Plotly . animate ( gd , figure , animation_options ) ;
107
93
}
108
- return Plotly . react ( id , {
94
+ return Plotly . react ( gd , {
109
95
data : figure . data ,
110
96
layout : clone ( figure . layout ) ,
111
97
frames : figure . frames ,
112
98
config : config ,
113
99
} ) . then ( ( ) => {
100
+ const gd = this . gd . current ;
101
+
102
+ // double-check gd hasn't been unmounted
103
+ if ( ! gd ) {
104
+ return ;
105
+ }
106
+
107
+ // in case we've made a new DOM element, transfer events
108
+ if ( this . _hasPlotted && gd !== this . _prevGd ) {
109
+ this . _prevGd . removeAllListeners ( ) ;
110
+ this . _hasPlotted = false ;
111
+ }
112
+
114
113
if ( ! this . _hasPlotted ) {
115
- // double-check gd hasn't been unmounted
116
- const gd = document . getElementById ( id ) ;
117
- if ( gd ) {
118
- this . bindEvents ( ) ;
119
- Plotly . Plots . resize ( gd ) ;
120
- this . _hasPlotted = true ;
121
- }
114
+ this . bindEvents ( ) ;
115
+ Plotly . Plots . resize ( gd ) ;
116
+ this . _hasPlotted = true ;
117
+ this . _prevGd = gd ;
122
118
}
123
119
} ) ;
124
120
}
125
121
126
122
extend ( props ) {
127
- const { id , extendData} = props ;
123
+ const { extendData} = props ;
128
124
let updateData , traceIndices , maxPoints ;
129
125
if ( Array . isArray ( extendData ) && typeof extendData [ 0 ] === 'object' ) {
130
126
[ updateData , traceIndices , maxPoints ] = extendData ;
@@ -143,20 +139,21 @@ class PlotlyGraph extends Component {
143
139
traceIndices = generateIndices ( updateData ) ;
144
140
}
145
141
146
- return Plotly . extendTraces ( id , updateData , traceIndices , maxPoints ) ;
142
+ const gd = this . gd . current ;
143
+ return Plotly . extendTraces ( gd , updateData , traceIndices , maxPoints ) ;
147
144
}
148
145
149
146
graphResize ( ) {
150
- const graphDiv = document . getElementById ( this . props . id ) ;
151
- if ( graphDiv ) {
152
- Plotly . Plots . resize ( graphDiv ) ;
147
+ const gd = this . gd . current ;
148
+ if ( gd ) {
149
+ Plotly . Plots . resize ( gd ) ;
153
150
}
154
151
}
155
152
156
153
bindEvents ( ) {
157
- const { id , setProps, clear_on_unhover} = this . props ;
154
+ const { setProps, clear_on_unhover} = this . props ;
158
155
159
- const gd = document . getElementById ( id ) ;
156
+ const gd = this . gd . current ;
160
157
161
158
gd . on ( 'plotly_click' , eventData => {
162
159
const clickData = filterEventData ( gd , eventData , 'click' ) ;
@@ -212,9 +209,10 @@ class PlotlyGraph extends Component {
212
209
}
213
210
214
211
componentWillUnmount ( ) {
215
- const gd = document . getElementById ( this . props . id ) ;
212
+ const gd = this . gd . current ;
216
213
if ( gd && gd . removeAllListeners ) {
217
214
gd . removeAllListeners ( ) ;
215
+ Plotly . purge ( gd ) ;
218
216
}
219
217
window . removeEventListener ( 'resize' , this . graphResize ) ;
220
218
}
@@ -263,6 +261,7 @@ class PlotlyGraph extends Component {
263
261
< div
264
262
key = { id }
265
263
id = { id }
264
+ ref = { this . gd }
266
265
data-dash-is-loading = {
267
266
( loading_state && loading_state . is_loading ) || undefined
268
267
}
@@ -280,6 +279,7 @@ const graphPropTypes = {
280
279
* components in an app.
281
280
*/
282
281
id : PropTypes . string ,
282
+
283
283
/**
284
284
* Data from latest click event. Read-only.
285
285
*/
@@ -673,10 +673,8 @@ const graphDefaultProps = {
673
673
config : { } ,
674
674
} ;
675
675
676
- GraphWithDefaults . propTypes = graphPropTypes ;
677
676
PlotlyGraph . propTypes = graphPropTypes ;
678
677
679
- GraphWithDefaults . defaultProps = graphDefaultProps ;
680
678
PlotlyGraph . defaultProps = graphDefaultProps ;
681
679
682
- export default GraphWithDefaults ;
680
+ export default PlotlyGraph ;
0 commit comments