haunted
haunted copied to clipboard
Virtual component never unmounts
I've found an issue when using virtual components. The teardown function returned from useEffect is never invoked. Also, state remains after a virtual component is unmounted.
The following sandbox uses the same function as both a real component and a virtual component.
When showing/hiding the real component, the count is reset, as expected. However, when showing/hiding the virtual component, the count remains the same.
Also, as you can see from console.log, the virtual counter's useEffect teardown is never invoked.
https://codesandbox.io/s/mzvrmj2z99
I noticed issue #42, but I don't know if this is related, so I created a new issue.
Oh, so that was it. I was having problems to integrate haunted with my router and showing a loader between page navigations. The loader didn't disappear on a second time.
@nicolasparada Just out of curiosity, why are you using virtual components? What's the appeal to you? Again, just curious because I don't use them personally :)
Thanks for the sample @lucaseverett! I'll take a look at this tomorrow. Can't commit on a fix but having a sample makes it 1000x easier!
Not much. I'm not calling these components with the tag name but with the function. I don't need the connected/disconnectedCallback if I'm using useEffect. And I still cannot get around shadow DOM. I like my CSS to cascade.
Virtual components are more simple 🙂
I'm mean... I'm doing all in JavaScript so is no much difference doing:
html`
<some-component .prop=${value}></some-component>
`
Than:
html`
${SomeComponent({ prop: value })}
`
I don't need the tag name is what I'm trying to say.
@nicolasparada Would you be more included to use components if you didn't have to define a tag name?
import { define, component } from 'haunted';
define(component(() => html` ... `))
Where define is a new thing that doesn't exist yet :)
Not really. withHooks is all I need 🙂
I think I have a fix for this. Working on test. Might be tomorrow before its in.
Fix is in #70 which I plan to release tomorrow morning if there are no objections.
Fixed in 4.1.4
I have a very funny bug. The thing is... it doesn't work when I try it locally, but it works when I upload it to netlify. github.com/nicolasparada/reprod-haunted-routing
Yet it works perfectly fine when using component().
Ok, let's reopen, i'll try to take a look at that one too.
I'm still seeing an issue with my original example, if the Main component is used as a virtual component with lit-html render instead of mounted as a real component.
https://codesandbox.io/s/23m15p5rwy
The issue I'm seeing is that the teardown effect isn't ran on the first unmount of the virtual component, but subsequent mount/unmount works as expected.
The state is being reset, but if you watch the console you will see that the actual teardown effect isn't being invoked.
There is also something weird happening if the same virtual component is used multiple times.
https://codesandbox.io/s/1qkzn946m7
Try increasing both counts and then hide the first one. The second one resets as well.
By the way, Andrea is having some similar issues with Neverland.
https://github.com/WebReflection/neverland/issues/20
Been on vacation, will try to take a look in the next few days.
Hey, I have a fork with tests that demonstrate these issues and more.
I have found in @lucaseverett's 2 counter example that if you
- hide the first counter
- increment the second counter
- hide the second counter
- unhide the second counter
It will show 1 instead of the expected 0. I made a test for this but it fails early because of the other issue with the second counter being reset on the first counter's hide.
I also found that unmount calls never fire if there is any form of indirection. For example if you mount a template with a virtual component in it and then remove that template, the virtual component's unmount function is never called.
I have not thought too much about how to solve this but I wanted to get some tests that demonstrate these bugs.
@matthewp, do you think you'll be able to resolve these issues? A sound virtual component w/ hooks feels out of reach to me.
I just spent a long time ~~pointing~~ poking around with the virtual implementation. I am not convinced that an effect hook is possible for virtual components in lit-html. I can't figure out how to get a reference to the actual dom node for a virtual component.
When looking at what part gives you, it's not helpful. part.startNode is the first child of the node that the component will be inserted into. You can traverse up the tree to the root node of this piece of dom and add a mutation listener to it and tell the observer to observe that node and the whole subtree. The root turns out to be a temporary template unless the component is being mounted directly with render. The only mutation observed is all the current nodes being removed from the temporary template.
Therefore you never get a reference to the dom being rendered by the component or the actual host node of the finally rendered html.
There might be some weird work around you could do like observing the nodes being removed and then wait till they have been inserted into the final html somehow. Then observe their new parent and try to figure out which node is the result of your component. Than trigger clean up when the node is removed. But that seems super convoluted and extremely fragile.
If that wasn't hard enough your component can be unmounted indirectly like when it's parent is removed from the dom. Meaning you would have to observe all dom removals and traverse down every tree being removed and check if your node is a part of the tree. This would be a huge amount of overhead with every removal of dom and I think that it would erase any performance advantage of lit-html.
We might have to wait until lit-html adds apis to allow us to access the actual dom being rendered by a template. And then it still might not be a good idea.
Thanks for the research @asafigan!