widgetti / reacton

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

Simple example of reacton as a class #9

Closed DougRzz closed 11 months ago

DougRzz commented 1 year ago

Hi, I feel this project should really help me but I am struggling to get how it would be beneficial compared to a class type structure. Also, would there be a benefit to using Reacton within a class structure? I couldn't get that to work. I would very much appreciate your thoughts on the following button click examples below. In particular, how can Reacton work in classes? and would that structure typically be used in large Jupyter widget web-app projects?

Old school method (taken from Reacton docs)

import ipywidgets as widgets

clicks = 0  # issue 3
def on_click(button):
    global clicks  # issue 3
    clicks += 1
    button.description = f"Clicked {clicks} times"     
button = widgets.Button(description="Clicked 0 times") 
button.on_click(on_click)  #
button

Reacton (taken from Reacton docs)

import reacton

@reacton.component
def ButtonClick():
    # first render, this return 0, after that, the last argument
    # of set_clicks
    clicks, set_clicks = reacton.use_state(0)

    def my_click_handler():
        # trigger a new render with a new value for clicks
        set_clicks(clicks+1)

    button = w.Button(description=f"Clicked {clicks} times",
                      on_click=my_click_handler)
    return button

ButtonClick()

Old school method BUT in a class

The method I currently use for large projects.


import ipywidgets as widgets

class ButtonClick:
    def __init__(self):
        self.clicks = -1  # issue 3
        self.button = widgets.Button() 
        self.button.on_click(self.on_click)  
        self.on_click(_)

    def on_click(self,_):
        self.clicks += 1
        self.button.description = f"Clicked {self.clicks} times"     
ButtonClick().button

My attempt using Reacton in a class but it doesn't work.

Is this even needed? I'm not sure. It would be nice to incorporate Reacton in existing code within a class based structure.

class ButtonClick_r:
    def __init__(self):
        self.ButtonClick()
        self.clicks= 0
        self.button = w.Button(description=f"Clicked {self.clicks} times",
                          on_click=self.my_click_handler)        
    @reacton.component
    def ButtonClick(self):
        # first render, this return 0, after that, the last argument
        # of set_clicks
        self.clicks, set_clicks = reacton.use_state(0)

    def my_click_handler(self):
        # trigger a new render with a new value for clicks
        set_clicks(self.clicks+1)

ButtonClick_r().button
maartenbreddels commented 1 year ago

Also, would there be a benefit to using Reacton within a class structure?

We have discussed this quite a bit. I think the only benefit is that it doesn't look so unfamiliar to Python users (I guess that is also what drives you to a class, right?).

The downside of a class is that we need to be clear about the lifetime of self, and can its properties be mutated? We can mirror this from the ReactJS world, but it also requires a decorator to make sure that outside of the class, it's state cannot be mutated (otherwise, why no stay with regular ipywidgets).

This is a very interesting topic, however, with no easy answers, so any feedback (like this) is very welcome.

My main question to you is, why would you want this? Is there a benefit from using classes over a (decorator) function?

DougRzz commented 1 year ago

My main question to you is, why would you want this? Is there a benefit from using classes over a (decorator) function?

I'm not sure I can answer that well. A class structure generally helps to provide a clean API to programmatically control or interact with the web-app. By having the Widgets outside of this class structure would make it harder for variables to be passed between the widgets and would be harder to produce clean reliable code.

Generally speaking, I am trying to determine some best practices for to develop Python web-apps. We are currently using a combination of Dash, Streamlit and Voila. Streamlit is great for small apps and has a consistent look and feel. But Voila (with Ipyvuetify) is better for larger more complex web-apps. But having multiple web-app frameworks and no consistent standards has caused a bit of a maintenance nightmare. So I would like to reduce the number of web-app frameworks (ditch Streamlit and Dash and only use Voila) and create coding standards for the Voila apps. However, Jupyter Widgets requires more callback functions than a Streamlit app, thus Voila is perceived to be harder to setup so it might be hard to sell this idea to the team. I was hoping Reacton would ease those pains and help our transition to Voila only web-apps.

I would love to see a showcase example of a large Voila or Jupyter Widget project, ideally using a mixture of ipyvuetify, plotly, bqplot etc. Perhaps I should evaluate Solara! I've just been looking through the Solara tutorials. I would be very interested in how an experienced software developer would structure a big project. But there doesn't seem to be many teams able to share those projects (which must be frustrating for you).

maartenbreddels commented 1 year ago

A class structure generally helps to provide a clean API to programmatically control or interact with the web-app. By having the Widgets outside of this class structure would make it harder for variables to be passed between the widgets and would be harder to produce clean, reliable code.

I think that arbitrary interaction (n objects can have n^2 interactions) makes it very difficult to understand larger applications. Or am I misunderstanding what you meant?

In the case of Reacton, there are no widgets outside of any structure. The elements (which replace the widgets) are completely isolated in the scope of the function, nobody can mess with them except the component itself. I think this 'limitation' makes it easier to reason about a component.

I was hoping Reacton would ease those pains and help our transition to Voila only web-apps.

I am personally convinced by this. Actually, I think it's more Solara that will help you here since Reacton is quite bare-bone.

There is one project that we work on that is source-visible, but not open source: https://github.com/dominodatalab/low-code-assistant (docs at https://dominodatalab.github.io/low-code-jupyter-docs/ with some screen capture available). (Again, not open source, it has copyright, but it may give an idea of how to structure a larger application)

Without Solara, we (@mariobuikhuizen and I) would be unable to create an app like this, or with way more time, more bugs, and a slower pace.

I do plan to write more documentation on our views on how to write re-usable components/libraries, and how to write larger apps (they have different requirements/needs).

If you don't mind, I'd like to do a video call with you to discuss this some more, is that ok? (If possible, can you share your email address with me at maartenbreddels@gmail.com)

maartenbreddels commented 1 year ago

Note that low-code-assistant by domino is now called domino code assist and is no longer source visible either. Solara, and the website at https://github.com/widgetti/solara/ is an example of a larger (open source) project.