-
-
Notifications
You must be signed in to change notification settings - Fork 681
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
Fixing scope handling on dynamic children #802
Comments
Seems like a small price to pay for the potential to fix the problems you mentioned? 🤔 |
I said it elsewhere, but if that's what we need to do to make things work right, then break away! |
So, it turns out sometimes there’s a good reason to sit and let things stew. Thinking about the discussion in #880 and well as a comment from @novacrazy (I think) that we should be able to detect the current scope programmatically led me to realize that the much better solution here is (perhaps unsurprisingly) to do what Solid does: associate cleanups with effects (or memo), rather than with a Scope, and run them every time the effect runs. Because conditional/dynamic logic will always end up running within an effect (or memo), this means automatic, correct cleanup without manual scope manipulation. Apparently it took me nine months and a couple bug reports to understand why Solid is designed this way but now it makes perfect sense. We could probably do the same thing with contexts and ownership for signals/etc., which may lead to the eventual abolition of explicit For now, since this will still be a change in the framework’s behavior it will be part of 0.3.0, but I do not expect user code to have to change to accommodate it. Edited to add: If contexts were associated with effects instead of scopes we could have avoided #542 but I think we might need context provider components to wrap children in an effect instead of a simple |
Closed by #918. |
This is basically a "yeah, I hadn't quite thought that all the way through..." issue.
The Problem
Currently, you can render a dynamic child node like this:
or this
This is all well and good. However, consider the following example
In this example,
disappearing
never logs, even though the<Child/>
is added and removed.What's Happening?
So what's happening here?
DynChild
(the internal representation for this kind of thing) does create a child scope, which it uses to call.into_view()
when building the child contentleptos/leptos_dom/src/components/dyn_child.rs
Lines 191 to 192 in 64e056f
And it correctly disposes of that scope
leptos/leptos_dom/src/components/dyn_child.rs
Lines 200 to 201 in 64e056f
However, this
Scope
is actually not theScope
that the<Child/>
is rendered with.In particular, this line is a problem.
cx
here is moved in from<App/>
.move ||
into the view creates aDynChild
. When it renders its content, it creates and disposes child scopes descended from<App/>
.<Child/>
component creates a child scope, and renders<Child/>
in it. But note that inview! { cx, <Child/>
, thecx
is the<App/>
scope.This means that these two are siblings, not parent and child.
The Solution
The correct design for this is fairly simple: rendering a dynamic child should require a function that takes a
Scope
as its first argument, like thisor this
In this case, because the
cx
is not moved into the closure from<App/>
but lazily provided byDynChild
, the chain is correctly orderedThis means that when
DynChild
disposes of the created child scope, it will correctly clean up.Discussion
This has the potential to reduce the complexity of scope disposal code in the router and possibly the
<For/>
component. It will also fix this bug, and probably some unknown others.On the other hand, it's a breaking change to pretty much every example, piece of documentation, and user application.
The text was updated successfully, but these errors were encountered: