plotly / react-plotly.js

A plotly.js React component from Plotly 📈
MIT License
1.02k stars 136 forks source link

Plotly selection state is lost when re-rendering. #147

Open DylanVann opened 5 years ago

DylanVann commented 5 years ago

This codepen shows the issue: https://codepen.io/dylanvann/pen/OYaedz

The onHover handler forces a re-render.

Whenever the Plot is re-rendered some state is lost. In particular the selection is lost.

How it behaves when re-rendering (selection box is not maintained):

selection

How it behaves when not re-rendering (selection box is maintained):

better-selection

nicolaskruchten commented 5 years ago

You should be able to use uirevision to avoid most such problems: https://plot.ly/javascript/uirevision/

DylanVann commented 5 years ago

It does not seem to work for selection state.

nicolaskruchten commented 5 years ago

In your codepen, what does the problem look like? Can you post a gif? I can't replicate any issues on this end...

DylanVann commented 5 years ago

Posted a couple gifs in the description.

nicolaskruchten commented 5 years ago

Oh I see, the selection box disappears. Yes, this is not something which is persistent in plotly.js at the moment (it's not part of the figure spec at all actually) ... issue: https://github.com/plotly/plotly.js/issues/1851

DylanVann commented 5 years ago

Cool. Ideally to fit React's model selection could even be a controlled prop.

nicolaskruchten commented 5 years ago

Yes, this would be ideal but the underlying Plotly.js library doesn't really work in such a way as to be compatible with this. The idea behind uirevision is for it to be more controllably uncontrolled :)

yifei-zhan commented 5 years ago

@DylanVann you can control the constraint-range + range + selectedpoints property of traces to maintain the select state yourself, but it takes a lot of time. The api is not programmer-friendly

will-moore commented 4 years ago

Just ran into this issue myself. When I drag to select a region on the scatter plot, I want to show the selected items so I'm using onSelected to update the selected IDs in a parent Component. But unfortunately this state change is causing the <Plot/> inside the <PlotContainer /> to re-render and so I don't see the selection change in the Plot:

Component = () => {
    const [selectedIds, setSelectdIds] = React.useState([]);

    return (
        <div>
            <PlotContainer setSelectdIds={setSelectdIds} />
            <ShowSelected selectedIds={selectedIds} />
        </div>
    )

Any pointers or workarounds would be much appreciated? Thanks.

will-moore commented 4 years ago

Actually, I re-read the issue above and realise that was really concerned about the selection box but I'm not too bothered about the box. For me, I lose the selection itself. Is there a way to preserve the selected points? I've used all the state management described at https://github.com/plotly/react-plotly.js#state-management to preserve the axis ranges etc but I don't see selected objects in any of that state?

matthias-ccri commented 4 years ago

It would be good to get the selection as an input props. I was hoping to use react-plotly. I'm implementing something like crossfilter where the plot selections/brushes are a key part of the interface.

However it sounds like the underlying plotly library doesn't have robust selection support. Is there some workaround, so I can use react-plotly instead of d3? — maybe overlaying my own selection box outside of plotly, but then it would have to synchronize with the margins and scales.

will-moore commented 4 years ago

I'm using Plotly with crossfilter, and it seems that sometimes I am losing the current selection because of a re-render and sometimes not. Haven't got to the bottom of it yet.... https://github.com/will-moore/parade-crossfilter/blob/master/src/plots/Plot.js is my wrapper to maintain axis ranges on re-render. The parent https://github.com/will-moore/parade-crossfilter/blob/master/src/plots/ScatterPlot.js listens for when the crossfilter filter changes to update the plot. Based on React-crossfilter example at https://www.lighttag.io/blog/react-dc-js/.

m-podlesny commented 4 years ago

@DylanVann Is your problem resolved? Shall we close this issue?

DylanVann commented 4 years ago

As far as I'm aware it is not.

mlisthenewcool commented 3 years ago

Any update on this issue ?

juls-k commented 2 years ago

I have same issue, In React when it's re-rendering.

sellings-dev commented 1 year ago

I see this is an old issue, but I've been recently successful in working around it by using the selections attribute from the plot's layout object. The idea was to use it as a controlled prop as discussed earlier.

Essentially, I have the onSelected callback set a state on the parent component. The state is set to be event.selections. I then pass this same state as a prop for the plot component and make it the value of the selections attribute. To clear the selection, I just set the state to an empty array. Something like this:

// Parent Component

function ParentComponent(){
  const [selections, setSelections] = React.useState([])

  const handleSelect = ( event ) => {
    setSelections( event.selections )
  }

  const handleDeselect = ( event ) => {
    setSelections([])
  }

  return (
    <ChildComponent selections={selections} handleSelect={handleSelect} handleDeselect={handleDeselect} />
  )
}

// Child Component

function ChildComponent({selections, handleSelect, handleDeselect}){
  return (
    < Plot
      data={...}
      layout={{
        ...,
        selections: selections,
        ...
      }}
     onSelected={handleSelect}
     onDeselect={handleDeselect}
    />
  )
}