widgetti / reacton

A pure Python port of React for ipywidgets
https://reacton.solara.dev/
MIT License
298 stars 18 forks source link

SelectionRangeSlider Options List Not Updating #12

Closed brendan0powers closed 1 year ago

brendan0powers commented 1 year ago

In some cases when changing the options of a SelectionRangeSlider, the options list is not updated. For this to occur, you must have both continuous_update set to True, and an index specified. I've seen similar behavior in react_ipywidgets 0.11 (ipywidgets 0.7) with the SelectMultiple widget. I believe it's some kind of race condition in the SelectionRangeSlider's JavaScript view, but I haven't been able to reproduce this issue with ipywidgets directly.

Test Case: Click Update, and then try and adjust the slider range. The range remains 1-3, instead of updating to 1-10. Set continuous_update to False, or remove the index, and the problem does not occur.

import reacton as react
import reacton.ipywidgets as w

@react.component()
def Test():
    options, setOptions = react.use_state(['1', '2', '3'])

    firstIndex = 0
    lastIndex = max(0, len(options)-1)

    def onClicked():
        setOptions([str(i) for i in range(1, 11)])
        print("what")

    print(options)

    with w.VBox() as vbox:
        w.Button(description="Update", on_click=onClicked)
        w.SelectionRangeSlider(
            description="Options",
            options=options,
            index=(firstIndex, lastIndex),
            continuous_update=True
        )

    return vbox

Test()

Software Versions:

python                          3.10.9
ipywidgets                    8.0.4
reacton                          1.2.2
maartenbreddels commented 1 year ago

I think this is an ipywidget bug.

import ipywidgets
options = ['1', '2', '3']
firstIndex, lastIndex = 1, 2

s = ipywidgets.SelectionRangeSlider(
            description="Options",
            options=options,
            index=(firstIndex, lastIndex),
            continuous_update=True
        )
def update(b):
    s.options = [str(k) for k in range(1,11)]
b = ipywidgets.Button(description="Update")
b.on_click(update)
display(b, s)

Since this behaves the same, do you agree?

Actually, Reacton can help you work around it, by using:

        w.SelectionRangeSlider(
            description="Options",
            options=options,
            index=(firstIndex, lastIndex),
            continuous_update=True
        ).key(repr(options))

Where the .key(..) function will make reacton not match this element to the widget with the previous options. It will create a new widget and destroy the old one. I guess there are better way that repr(options) though.

brendan0powers commented 1 year ago

Yeah, seems likely to be a ipywidgets bug. That example does not reproduce the issue for me, but I suspect it's a race condition on the ipywidgets browser side. I was unable to find a case that reproduced it before now. I'll file a bug with ipywidgets and include your example and a reacton example. Hopefully that's enough for them to go on.

The .key() workaround works great! I use id(optoins) since I replace the options array each it changes. Thanks!

maartenbreddels commented 1 year ago

Yes, id(..) in this case should be fine since the list will be created before the old once can be GC'ed, so you cannot end up with the same id/address.

maartenbreddels commented 1 year ago

I think the issue is solved.