Open willingc opened 5 years ago
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).
@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.
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?
Why I care: https://github.com/jupyterlab/jupyterlab/issues/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.
@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.
@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.
@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.
@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?
@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).
@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.
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!
@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.
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.
From #67
Do we want to have a discussion about or make a design decision on how we want to support updated displays of vdom objects?