Pauan / rust-dominator

Zero-cost ultra-high-performance declarative DOM library using FRP signals for Rust!
MIT License
967 stars 62 forks source link

Add DomBuilder.child_signal() #18

Closed njam closed 4 years ago

njam commented 4 years ago

Idea for rendering complete Dom nodes when a signal changes.

Could be useful for conditionally rendering large parts of an application.
For example top-level routing based on the URL path:

html("div")
    .child_signal(state.path.signal_cloned()
        .map({
            let state = Rc::clone(self);
            move |path| {
                match path.as_str() {
                    "/foo" => {
                        html("div").text("foo page!").into_dom()
                    }
                    "/bar" => {
                        html("div").text("bar page!").into_dom()
                    }
                    _ => {
                        html("div").text("error page!").into_dom()
                    }
                }
            }
        })
    )
    .into_dom()
Pauan commented 4 years ago

Thanks for this, but I would prefer it if it were implemented like this:

#[inline]
pub fn children_signal<B>(self, children: B) -> Self
    where B: Signal<Item = Vec<Dom>> + 'static {
    self.children_signal_vec(children.to_signal_vec())
}

...but I'm not sure if we should have this method at all. Using SignalVec is usually faster, so I'm worried about people seeing the children_signal method and using it without realizing that it's slow.

Perhaps if we had very clear warnings in the documentation about the performance issues, then it would be okay.

njam commented 4 years ago

I see! If it's faster using SignalVec, then I guess this function is not needed, and I will just use to_signal_vec() in the application code, as you suggest in https://github.com/Pauan/rust-dominator/issues/19#issuecomment-549102517.

Pauan commented 4 years ago

To be clear, the reason why SignalVec is faster is because when you do things like push, insert, remove, etc. it will only change 1 DOM node, so it's constant time, no matter how many children there are.

But with to_signal_vec it will always remove all of the existing children and replace them with new DOM nodes, on every single change. That means it's linear time (and removing/inserting DOM nodes is expensive).

So if you're creating a small number of DOM nodes (or the signal rarely changes), then to_signal_vec is perfectly fine. But for even moderately large lists I recommend using SignalVec.

Doing URL routing is one of the rare situations where to_signal_vec is perfectly appropriate, because you really do want to replace everything when the URL changes.