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

Pure components (memoizable components) #1011

Open
mofojed opened this issue Nov 11, 2024 · 3 comments
Open

Pure components (memoizable components) #1011

mofojed opened this issue Nov 11, 2024 · 3 comments
Labels
enhancement New feature or request
Milestone

Comments

@mofojed
Copy link
Member

mofojed commented Nov 11, 2024

As a developer I would like to be able to memoize each component, such that it does not re-render in the render cycle if none of the props have changed.

There should be two ways to do this:

  1. Use a ui.use_memo to wrap returning an element, such that the element is not re-rendered if the props have not changed. E.g. something like this should work, and not call the slow_component whenever a new colour is selected:
from deephaven import ui
import time


@ui.component
def my_slow_form(on_submit):
    print("SLOW COMPONENT RENDER...")
    time.sleep(1)
    return ui.form(
        ui.text_field(label="Name", name="name"),
        on_submit=on_submit
    )


@ui.component
def my_app():
    bg, set_bg = ui.use_state('red')

    handle_click = ui.use_callback(lambda e: print(f"Form submitted: {e}"), [])

    # memoize rendering the slow form with a `use_memo`
    slow_form = ui.use_memo(lambda: my_slow_form(handle_click), [handle_click])

    return ui.view(
        ui.picker("salmon", "lemonchiffon", "green", "red", selected_key=bg, on_change=set_bg),
        slow_form,
        background_color=bg,
        padding="size-100"
    )

app = my_app()

However, in that above example it is re-rendering the slow form each time the colour is changed. That's because the ui.use_memo wraps creating the renderable element, but the element isn't actually rendered until a render cycle (from the user) anyway.
2. Add a is_pure (or memoize?) flag to the @ui.component wrapper (defaulting to false), e.g.

from deephaven import ui


@ui.component(is_pure=True)
def my_pure_component(param1: str, param2: str):
    ...

Indicating it is a pure component should ensure that it does not re-render the component unless one of the provided props has changed.

@mofojed mofojed added enhancement New feature or request triage labels Nov 11, 2024
@mattrunyon
Copy link
Collaborator

React just has memo for functional components. Do we want to use something like that instead of extending it to use_memo? I don't think we'd have a good way of differentiating between use_memo for a value and use_memo for a memoized component.

@mofojed
Copy link
Member Author

mofojed commented Nov 12, 2024

@mattrunyon see my second proposal above, very similar. Or we could add ui.memo as well and then it would just be used as another decorator on top, eg.

@ui.memo
@ui.component
def my_pure_component(...):

Come to think of it... We should almost just do the same thing with callbacks within a component... E.g.

@ui.component
def my_component():

    @ui.callback(value)  # deps as args in decorator
    def my_callback():
      print("value is", value)

    # vs use_callback now
    def _my_callback():
       print("value is", value)
    my_callback = ui.use_callback( _my_callback, [value])
    ...

@mattrunyon
Copy link
Collaborator

That's not a bad idea since Python limits lambdas to a single expression. And Python already has cache decorators in functools, so it's probably not an uncommon pattern to Python devs to use decorators for caching

@vbabich vbabich added this to the December 2024 milestone Nov 12, 2024
@vbabich vbabich removed the triage label Nov 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants