-
-
Notifications
You must be signed in to change notification settings - Fork 53
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
Fix memory management gotcha #33
Comments
Well, damn, this is indeed really hard. Let's summarize the problem once again: when we create a Laminar element like so:
we immediately create the required subscriptions for The solution I was hoping to get working is similar to def makeDiv(textSignal: Signal[String]): Div = {
val el = div("Number of people:")
val textState = textSignal.toState(owner = el) // imagine there's a reason to make it a State
el <-- child.text <-- textState
el
} Same situation as before – simply calling that method and ignoring its output is enough to cause a memory leak ( However, observe how we made Basically, to solve this memory management issue for good, we can't have Laminar elements remain Owners. Perhaps this calls for introduction of some kind of This could potentially improve performance too, as we would only need to keep track of Components' root elements mounted status. I do not want to give up Laminar's compositional freedom. Currently you do not need to wrap your code in any wrappers, and can freely use functions / classes / objects / whatever to build your components. React's notion of components does not suit me at all, it's too constrained. Less radically, I think perhaps we could introduce a Another option could be to provide something of a val el: Div = div.whenMounted { (owner, thisNode) =>
val textState = textSignal.toState(owner) // imagine there's a reason to make it a State
List(
"Hey, "child.text <-- textState
)
} This seems like what people would generally intend state to work like, however this makes it hard to make a component that returns not just an element but also other data like perhaps some relevant streams that are generated from its state. So, this actually sounds like a use case for a well designed Component type. Hrmmmm. I need to sleep on this... repeatedly. |
The Kinda want to avoid going in that direction, having everything wrapped in a monadic type like that. |
A few more things for my own clarity:
|
Current problematic behaviour described in the docs:
I plan to address this by starting the element's subscriptions when the element is inserted into the DOM rather than when the element is created. That way, the elements that were created but never inserted into the DOM will not hold any leaky resources, and so can be discarded without worrying about the subscriptions.
This means that an element that has subscriptions will need to listen to its own
mountEvents
stream, but it already does that for the purpose of unsubscribing when the element gets unmounted (seeReactiveElement.onOwned
).This solution will fix the issues when using
ReactiveElement
'ssubscribe*
methods. However, as elements areOwner
s, they can also be used directly in methods where an implicit owner is required, which would still not be safe for the reason described above. Perhaps we need to avoid exposing elements asOwner
s, or maybe even extend Airstream's ownership API to allow for a delayed subscription start (basically, bring the subscribe* methods into the Owner?). This might also indirectly help us deal with the other memory management gotcha:However, this starts looking a lot like the dynamic subscriptions system we had in previous versions of Laminar, where subscriptions could be turned on and off as the element gets mounted and unmounted. We should investigate if we can bring back this system without falling into other kinds of memory leaks (esp. zombie references).
The text was updated successfully, but these errors were encountered: