I'm trying to upgrade my crossfilter example to be more declarative by using react() but seeing errors. This is my attempt: https://github.com/plotly/plotly.js-crossfilter.js/blob/23b1ff9bd8dc0a3923caf29e4d3feb960ec31234/script.js
While drag-selecting on top of the Infant Mortality chart, the dashed-line selection box does not appear, and errors appear in the console:
plotly-latest.min.js:7 Uncaught (in promise) TypeError: Cannot read property '0' of undefined
at Object.m.supplyDefaultsUpdateCalc (plotly-latest.min.js:7)
at Object.r.react (plotly-latest.min.js:7)
at redraw (script.js:64)
at n.hist_im_select (script.js:121)
at n.emit (plotly-latest.min.js:7)
at HTMLDivElement.t.emit (plotly-latest.min.js:7)
at plotly-latest.min.js:7
that one sounds similar to https://github.com/plotly/plotly.js/issues/2602, might have been fixed https://github.com/plotly/plotly.js/pull/2603
I'm using the latest CDN build so I don't think that's it. It would be really nice for this to at least have the same behaviour as in #2643 ... /cc @alexcjohnson :)
Ah, for clarity, here is the diff between the two cases: https://github.com/plotly/plotly.js-crossfilter.js/commit/23b1ff9bd8dc0a3923caf29e4d3feb960ec31234
Alright, managed to reproduce and understand what's going on here:
Plotly.react on the same plot, giving it new data arrays.Plotly.plot to know to recalc is to delete gd.calcdata.Plotly.plot bails out because you're in the middle of a drag:https://github.com/plotly/plotly.js/blob/master/src/plot_api/plot_api.js#L139-L145
Plotly.react the data arrays may have not changed, so we don't delete gd.calcdata, we expect it to be there to be relinked to the modified traces.gd.calcdataI could do various simple things to avoid the particular error seen here (recalc before bailing out of Plotly.plot, or use some other signal besides deleting gd.calcdata, or just check if gd.calcdata exists before trying to relink it) but all of those have problems of various severity with the plot data being out of sync with the displayed plot, which are going to bite us down the line in obvious or subtle ways.
I think the solution is to make something like gd._hoverData and Plots.rehover but for dragging, so we can get rid of that gd._dragging bail out, actually do the replot, then re-establish the partial drag (be it a pan, a zoom, a select...). Obviously this has the potential to be a bit of a project, given all the different dragmode settings; but it would also address the use case of streaming (or otherwise changing independent of user interaction) data.
@nicolaskruchten we talked about making the select box / lasso part of the figure - and that may still be part of this solution (just as we'd eventually like to have the hover data be part of the figure https://github.com/plotly/plotly.js/issues/1848, but I think the parts of the state that depend explicitly on mouse events (is the mouse up or down, where was it at mousedown) - which are necessary to fully recreate the plot after a mid-drag redraw - should not be part of the figure but should instead be kept as internal state. For one thing we don't really want the user to be able to say "start this plot out as though the mouse is down." For another, having this be internal state that we re-initialize after replot would allow us to clear it naturally if the change has obviated that state, like if the subplot was removed, dragmode was changed, anything like that.
@etpinard also brought up https://github.com/plotly/plotly.js/issues/1640 - an infinite loop created by using the hover event to do something that moves data points around (in that case adding/removing an annotation that changes the axis ranges) such that as soon as a point is hovered it is moved by the update so it's not hovered on, whereupon it's moved back again... I'll comment more over there, but similar loops could be an issue here too.
I think the solution is to make something like gd._hoverData and Plots.rehover
This sounds like the correct way forward.
we talked about making the select box / lasso part of the figure
Yes. This one is in https://github.com/plotly/plotly.js/issues/1851
(just as we'd eventually like to have the hover data be part of the figure #1848,
Can't wait for that one.
but I think the parts of the state that depend explicitly on mouse events (is the mouse up or down, where was it at mousedown) - which are necessary to fully recreate the plot after a mid-drag redraw - should not be part of the figure but should instead be kept as internal state
I agree 100% here.
On a related topic, now that we're marching towards making every piece of selection interactions part of the plotly.js state, that is selections will soon be "fully" recreatable using restyle and relayout, maybe we should start thinking about dropping plotly_select* family of events and replace them with soon-to-be equivalent restyle/relayout event data?
Stumbled on https://github.com/plotly/plotly.js/issues/2120 which also reports gd._dragged limitations.
Merged into https://github.com/plotly/plotly.js/issues/3305
Most helpful comment
Alright, managed to reproduce and understand what's going on here:
Plotly.reacton the same plot, giving it new data arrays.Plotly.plotto know to recalc is to deletegd.calcdata.Plotly.plotbails out because you're in the middle of a drag:https://github.com/plotly/plotly.js/blob/master/src/plot_api/plot_api.js#L139-L145
Plotly.reactthe data arrays may have not changed, so we don't deletegd.calcdata, we expect it to be there to be relinked to the modified traces.gd.calcdataI could do various simple things to avoid the particular error seen here (recalc before bailing out of
Plotly.plot, or use some other signal besides deletinggd.calcdata, or just check ifgd.calcdataexists before trying to relink it) but all of those have problems of various severity with the plot data being out of sync with the displayed plot, which are going to bite us down the line in obvious or subtle ways.I think the solution is to make something like
gd._hoverDataandPlots.rehoverbut for dragging, so we can get rid of thatgd._draggingbail out, actually do the replot, then re-establish the partial drag (be it a pan, a zoom, a select...). Obviously this has the potential to be a bit of a project, given all the differentdragmodesettings; but it would also address the use case of streaming (or otherwise changing independent of user interaction) data.@nicolaskruchten we talked about making the select box / lasso part of the figure - and that may still be part of this solution (just as we'd eventually like to have the hover data be part of the figure https://github.com/plotly/plotly.js/issues/1848, but I think the parts of the state that depend explicitly on mouse events (is the mouse up or down, where was it at mousedown) - which are necessary to fully recreate the plot after a mid-drag redraw - should not be part of the figure but should instead be kept as internal state. For one thing we don't really want the user to be able to say "start this plot out as though the mouse is down." For another, having this be internal state that we re-initialize after replot would allow us to clear it naturally if the change has obviated that state, like if the subplot was removed,
dragmodewas changed, anything like that.@etpinard also brought up https://github.com/plotly/plotly.js/issues/1640 - an infinite loop created by using the hover event to do something that moves data points around (in that case adding/removing an annotation that changes the axis ranges) such that as soon as a point is hovered it is moved by the update so it's not hovered on, whereupon it's moved back again... I'll comment more over there, but similar loops could be an issue here too.