Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simple example of reacton as a class #9

Closed
DougRzz opened this issue Jan 17, 2023 · 4 comments
Closed

Simple example of reacton as a class #9

DougRzz opened this issue Jan 17, 2023 · 4 comments

Comments

@DougRzz
Copy link

DougRzz commented Jan 17, 2023

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
Copy link
Contributor

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
Copy link
Author

DougRzz commented Jan 18, 2023

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
Copy link
Contributor

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
Copy link
Contributor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
2 participants