Matt-Esch / virtual-dom

A Virtual DOM and diffing algorithm
MIT License
11.65k stars 780 forks source link

Clone a tree #101

Open OliverJAsh opened 10 years ago

OliverJAsh commented 10 years ago

Is it possible to [deep] clone an existing virtual tree? Doing a standard deep clone on the object will not suffice because the virtual tree has a special object type.

Would it make sense to have modifier functions for trees which were immutable and returned cloned copies of the tree? E.g., you could have tree.withStyle('font-weight', 'bold'), instead of mutating the object at tree.properties.style. This would provide an immutable API for modifying trees.

neonstalwart commented 10 years ago

i'm definitely interested in some way to modify trees. my most recent need for something like this was being able to display a popup when a user clicked an arbitrary node. i'd love to be able to have the popup.render take any vnode and add a click handler to it rather than needing to wrap that node in another one with the handler or any number of other just as ugly workarounds.

Raynos commented 10 years ago

What about a h.extend(tree, { style: { fontWeight: 'bold' } }) that's basically an immutable deep extension of an existing vnode's properties.

neonstalwart commented 10 years ago

:+1: i had figured that what i needed should come from h due to the extra logic it has for parsing and applying events, etc.

h.extend with vtree-select might be a powerful combination for transforms.

mightyiam commented 9 years ago

I just came here after reporting https://github.com/parshap/vtree-select/issues/4 .

I attempted to clone a vtree and got vtree-select failing on it.

mightyiam commented 9 years ago

Excuse me, but, I can't find h.extend in virtual-hyperscript. Perhaps you meant it as a feature suggestion, @Raynos ?

neonstalwart commented 9 years ago

yes, it's a suggestion.

mightyiam commented 9 years ago

Thanks, @neonstalwart . +1 to that!

mightyiam commented 9 years ago

But, actually, I'm not sure if it is what I'm looking for. In my case, I would like to receive vtree input as a library and I would like to clone it or for it to be immutable so that it would no longer be subject to change by the user.

Raynos commented 9 years ago

@mightyiam

a vtree node is effectively immutable. All virtual-dom and mercury related code should treat it to be immutable.

The only reason it's not actually immutable, i.e. we don't call Object.freeze is because that is too expensive, causes large de-optimizations.

With that said, you can create a simple clone of a vtree if you wanted too, but that requires a deep clone that is prototype aware, that's all. Try ultradeepclone which will correctly set the prototype of the cloned object.

mightyiam commented 9 years ago

@Raynos Thank you. I'm developing a virtual-dom–based document editor for browsers. At the core of its state is a vtree, which represents the document.

In implementing undo/redo, I'm thinking:

But now I'm thinking that maybe things could go wrong with this, as even the ultradeepclone can't clone objects that were constructed with a constructor, because there's no access to that scope.

Can you please advise?

Raynos commented 9 years ago

@mightyiam you don't do undo/redo on vtree instances.

I recommend you create a function render(appState) { return vtree } that is pure. i.e. given the same appState it always returns the same vtree.

Then you can keep track of an immutable list of history for the appState. This way you just have to remember the last state of the application.

If you want to do any kind of time rewinding just render(oldAppState) and diff that against the current vtree.

You can see an example here ( https://github.com/Raynos/mercury/blob/master/time-travel.js ).

neonstalwart commented 9 years ago

:smile: i was just writing the same thing as @Raynos - use immutable state that you track and then derive your rendering from the state rather than tracking the rendering for each state. undo/redo is then just a matter of calling your render function on the relevant state object.

OliverJAsh commented 9 years ago

@mightyiam I don't know if this helps, but I know Scribe (a rich text editor) is looking to move to a virtual DOM. See https://github.com/guardian/scribe/issues/236.

Raynos commented 9 years ago

@OliverJAsh @mightyiam

ultradeepclone should work.

We do not do anything fancy in our constructors.

mightyiam commented 9 years ago

Thank you very much. Yes, indeed, ultradeepclone seems to provide clones of vtrees that are reusable. I can createElement out of them.

OliverJAsh commented 8 years ago

Here's what I ended up with:

function render(count)  {
    // Ultra deep clone then mutate. I wish we had an immutable virtual dom
    const newTree = udc(initialTree);
    newTree.properties.style = { color: 'hotPink' };
    return newTree;
}

I wish there was an immutable virtual tree object…

OliverJAsh commented 8 years ago

I wonder if we could come up with a way of merging two virtual trees, so you could do:

merge(h('div', 'foo'), h('div', { style: 'hotPink' }))