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

Design of updating existing display objects #83

Open
willingc opened this issue Dec 16, 2018 · 13 comments
Open

Design of updating existing display objects #83

willingc opened this issue Dec 16, 2018 · 13 comments

Comments

@willingc
Copy link
Member

From #67

  • Updating existing display objects does not work since we changed VDOM objects to be immutable; this breaks the last examples in exploring-elements.ipynb

Do we want to have a discussion about or make a design decision on how we want to support updated displays of vdom objects?

@rgbkrk
Copy link
Member

rgbkrk commented Dec 19, 2018

I think we need to re-do those examples, to instead make functions that return vdom elements. There's a part of me that thinks an API that allows updates could be built on top of vdom (just using vdom for the representation to send across the wire).

@rmorshea
Copy link
Contributor

rmorshea commented Dec 19, 2018

@rgbkrk I think @gnestor and I talked about this in the last month or so. In terms of underlying design it might be similar to what I did with purly (i.e. sending diffs over the wire) though with a very different api.

@allefeld
Copy link

Just stumbled across vdom, so maybe what I'm saying doesn't make sense.

But, the name "vdom" to me suggests a DOM interface like that in JavaScript, which is powerful exactly because it allows modifying DOM objects.

The current method to update, which involves recreating elements appears extremely cumbersome. Say I have a deep element hierarchy, and I want to change some small detail within, and I have to recreate the whole tree?!

I haven't tested it, but doesn't that inevitably lead to flicker, or parts of the notebook flipping up and down on update?

@allefeld
Copy link

Why I care: jupyterlab/jupyterlab#7873

I believe vdom has the potential to fill a big usability gap in JupyterLab, namely to

allow users to create interactive HTML-based displays with relatively little effort, especially without creating an extension

– but only if it enables in-place modification of elements.

@rmorshea
Copy link
Contributor

rmorshea commented Feb 16, 2020

@allefeld I’d take a look at my project idom (basically a second draft of purly mentioned above). It seems to do exactly what you’re looking for.

@gnestor
Copy link
Contributor

gnestor commented Feb 17, 2020

@allefeld VDOM (virtual dom) is a concept underlying React. The idea is that we don't need to directly manipulate the DOM (because cumbersome) and some would argue that we shouldn't. The jQuery way of doing things exemplifies this and apps built using jQuery are difficult to reason about and even more difficult to debug. To me, the core issue is jQuery encourages you to store app state in the DOM, spread out across a bunch of nodes. VDOM simplifies this by making UI a function of state ((state) => UIComponent(state)). This is great in the context of a Jupyter notebook because a user only needs to declare how their data maps to the virtual DOM and anytime their data changes, VDOM will update the DOM using the minimum DOM operations.

VDOM is not perfect but I've used it extensively and it's a heck of a lot better than using IPython.HTML. One of the best things about VDOM is the ability to create VDOM components and compose those components to create more elaborate UI. I recently added event handler support to VDOM so that you can create interactive UI with it, but there are some limitations to it that would probably require a rewrite. This allows you to create ipywidget-like interactive UIs with a fraction of the effort.

If you need to manipulate the DOM directly, then VDOM is not the right tool.

@allefeld
Copy link

@gnestor, thanks for the explanation!

The reason I posted is that I'm working on a Python object inspector (see jupyterlab/jupyterlab#7873), represented as nested unordered lists, and I need to create new sublists in response to user interaction (click). How would I go about implementing this using vdom? From what I can gather, I would have to recreate the whole nested lists every time, which indeed suggests that vdom is not the right tool.

@allefeld
Copy link

@rmorshea, thanks for suggesting idom, it indeed looks like it has exactly the functionality I want. What lets me hesitate is the need to create mini-webservers. That makes sense if the goal is to create Python-backed websites / apps, but suboptimal within Jupyter, where there's already a server running. Maybe I misunderstand?

@rmorshea
Copy link
Contributor

@allefeld IDOM does in fact require you to create a simple socket server to send DOM updates to the frontend because it's not inherently tied to the Jupyter ecosystem. With that said, if there were enough interest and I found the time, there's no reason that it couldn't integrate directly into Jupyter's comm interface (thus negating the need for a socket server).

@gnestor
Copy link
Contributor

gnestor commented Feb 17, 2020

@allefeld Theoretically, you would just update the display of the cell containing the JSON tree visualization with the new JSON for the Python variable (e.g. output_handle.update(json_component(new_json)))) and VDOM (more specifically React in the vdom jupyterlab extension) would handle computing the diff between the existing DOM and your new virtual DOM and apply the minimum number of DOM operations to get the DOM to reflect your new Python variable state.

@allefeld
Copy link

apply the minimum number of DOM operations to get the DOM to reflect your new Python variable state

Alright, that's the part I didn't realize. Thanks again!

@rmorshea
Copy link
Contributor

rmorshea commented Feb 17, 2020

@allefeld it may be worth noting that for "large" views there may be some performance limitations since:

  • The whole JSON view representation still needs to be sent over the network connection.

  • React still has to create the new "virtual DOM", compute the diff between it and the old one, and then perform the minimum set of operations on the "real DOM" (this is relatively cheap though).

I'm not really sure how "large" the view needs to be before you experience any slowdown though. In the past at least, when I've run tests locally with Jupyterlab, views with ~500 elements may started to take 0.1 seconds to update. @gnestor probably has a better sense of the performance limitations though.

@rmorshea
Copy link
Contributor

rmorshea commented Feb 17, 2020

Ok, here's an example which demonstrates these performance limitations. This 25x25 grid of colors shifts the colors in a row to the right when clicked. While for many use cases the performance is acceptable, it's definitely on the sluggish side. Scale this up to 50x50, and things really start to chug:

from vdom.helpers import div

colors = ["red", "blue", "purple", "green", "orange", "yellow"]
color_shift_state = {}

def color_grid(x_size, y_size):
    def update():
        handle.update(color_grid(x_size, y_size))

    return div(
        *[
            color_row(y_size, update, i, color_shift_state.get(i, 0))
            for i in range(x_size)
        ]
    )

def color_row(y_size, update, row_index, count):
    return div(
        *[color_row_item(update, row_index, i, count) for i in range(y_size)],
        style={"height": "15px"},
    )

def color_row_item(update, row_index, col_index, count):
    def shift_row_colors(event):
        color_shift_state[row_index] = count + 1
        update()

    return div(
        onClick=shift_row_colors,
        style={
            "backgroundColor": colors[(count + col_index) % len(colors)],
            "height": "15px",
            "width": "15px",
            "display": "inline-block",
        },
    )

handle = display(color_grid(25, 25), display_id=True)

If this same example were implemented using a solution like idom, it would not suffer the same performance limitations because it only sends the part of the view which actually changed over the wire.

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

No branches or pull requests

5 participants