Preact: Rendering directly to a DOM element

Created on 2 Mar 2018  路  8Comments  路  Source: preactjs/preact

I wonder why it is that render() accepts a VNode and a parent, rather than a target element?

It's inconsistent with how the diff() function works internally, and in some situations, this means you're forced to create a dummy parent wrapper-element, just to control where in the DOM your rendered elements end up.

While, in the case of simply adding an view to document.body, it's perhaps convenient to have render() automatically append to an empty container, it's not very practical, because the parent/child relationship of the arguments assumes a 1:1 relationship between the root of the component view and it's parent/wrapper element.

It would be much simpler and more elegant to have the external render() function work just like the internal diff() function, in terms of accepting a target DOM node, and the VNode state you'd like it to have.

It's not like the typical case of appending to document.body gets a whole lot more complex that way:

render(<App/>, document.body); // now

document.body.appendChild(render(<App/>)); // proposed

This would free us the need for dummy wrapper elements, and provides a lot more flexibility in cases where you have an existing element and want to update it directly.

For example, in this prototype I'm currently re-rendering all of the draggable panels, which are actually unrelated user-interfaces. I'll eventually change that, so that the panels themselves get created and managed individually, at which point the current API will force me to add a dummy wrapper element around every panel.

That's no disaster, obviously - it's just a bit silly, and it's not how Preact itself works internally in diff(), so it's a bit frustrating that convenience has to get in the way of correctness. The fact that the API ends up dictating HTML structure leads to real-world problems when designers deliver HTML and CSS, and a developer ends up having to change the HTML and CSS (and possibly breaking the design) in order to accommodate the framework.

This issue was widely debated here and ultimately lead to a similar API change in that library.

Thoughts?

discussion question

Most helpful comment

We have it this way, because react also does it the same way

Why do you want the panels to be managed outside of preact?

( btw: you should really use transform: translate(Xpx, Ypx) + will-change: transform instead of top and left)

All 8 comments

That's and interesting question!. I guess render acts like a shorthand and keeps consistency. Most of times you only have to use once to set the root of your preact app. And you don't want to append a child, you want to replace all contents within the target container. Dealing with diff directly would be, somehow, hard to provide all needed parameters, too much effor and driving confusion for new comers..

We have it this way, because react also does it the same way

Why do you want the panels to be managed outside of preact?

( btw: you should really use transform: translate(Xpx, Ypx) + will-change: transform instead of top and left)

Why do you want the panels to be managed outside of preact?

Fair question.

All of these panels will be movable by drag-and-drop. They will also have connectors, forming a graph, likely with connections in the thousands - all of the connectors will be drag-and-drop too.

During these drag-operations, only the moving connector or panel needs to be redrawn. Preact will be important, since, while you're dragging connectors between panels, the panels themselves may need updates, and Preact will do the hard work when panel states change.

But simply having a list of all panels and (worse) a list of all connectors, and expecting this to perform well, when every single panel and connector's view gets computed and diffed with every mouse-move event, does not seem like a realistic strategy. It's going to get sluggish. Even if it isn't horribly sluggish, it'll be substantial enough to interfere with the web-audio stream that needs as much available CPU and as little latency as possible.

I know about shouldComponentUpdate of course - but querying thousands of components for every mouse-move event to find out if they should update is bad enough, and is also redundant.

So I figured I'll have a more optimized strategy that accurately updates connectors and panels individually, while Preact can be in charge of the contents of the panels.

Unless you think you can propose a better strategy that leverages Preact for more of the hard work? Of course that would be ideal.

But Preact works with trees, like the DOM, and these are graphs, so computing necessary updates, as far as I can figure, is only possibly by querying long flat lists of connectors and panels - as opposed to some kind of pub/sub strategy, where the individual nodes in the graph can notify neighboring nodes of changes, which should be much more efficient.

Most of times you only have to use once to set the root of your preact app.

Not every use-case is a single app. (see above)

Dealing with diff directly would be, somehow, hard to provide all needed parameters

Agree, but that's not what I'm suggesting - only that you'd be able to target the element you want to update, directly, instead of having to target it's parent, which forces a 1:1 parent/child relationship.

We can't render without a parent, because mounting will happen prior to the tree being appended to the DOM.

To render into an empty tree, you can use a DocumentFragment:

let frag = document.createDocumentFragment()
render(<App />, frag)

There are also ways to change the 1:1 parent-child relationship too:

https://github.com/developit/preact-slots

https://github.com/developit/preact-portal

Well, you can create component and insert it where you want:

var myDom = preact.render(h(MyComponent));
document.body.appendChild(myDom);

P.S. like you have proposed

I do think @developit has provided a fair answer here

any other thoughts or safe to close? thanks <3

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jescalan picture jescalan  路  3Comments

nopantsmonkey picture nopantsmonkey  路  3Comments

kay-is picture kay-is  路  3Comments

Zashy picture Zashy  路  3Comments

paulkatich picture paulkatich  路  3Comments