When KeepAlive caches more items than its `max` prop allows, reactivated components may be erroneously marked as being unmounted
Vue version
3.2.45
Link to minimal reproduction
https://codesandbox.io/s/emit-issue-kezqfe
Steps to reproduce
Follow directions on reproduction site.
Click on "Create new page". Then click on the button in the blue box. Notice that it responds by showing "Clicked". Do this 4 more times so that there are 5 pages (make sure you do the final click). Now click on "Render page 4". Notice the click event does not get emitted all the way to the onClicked handler in Page.vue. Click on "Render page 3" and the button works again.
What is expected?
emit should work
What is actually happening?
emit does not work
System Info
System:
OS: macOS 12.6
CPU: (12) x64 Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
Memory: 20.04 MB / 32.00 GB
Shell: 5.8.1 - /bin/zsh
Binaries:
Node: 16.13.1 - ~/.nvm/versions/node/v16.13.1/bin/node
Yarn: 1.22.0 - /usr/local/bin/yarn
npm: 8.1.2 - ~/.nvm/versions/node/v16.13.1/bin/npm
Browsers:
Chrome: 108.0.5359.98
Edge: 108.0.1462.46
Firefox: 106.0.3
Safari: 16.2
npmPackages:
vue: 3.2.41 => 3.2.41
Any additional comments?
This was broken by this commit https://github.com/vuejs/core/pull/5679. If I remove the 2 lines added
if (instance.isUnmounted)
return;
then it works once again.
As this only happens when a keep-alive has more children than max
allows do cache ... I think there's a different underlying problem, surfaced by the fix in #5679
Keep-Alive children when 5 children have been created:
KeepAlive has 4 children. key=5 is active, key=4 is inactive
after switching to page 4:
- Page 4 is being shown in the browser
- but it still shows as
inactive
in the devtools.
Logging that Page 4 component to the console, we see this:
- The component is marked as being deactivated (which it was previously, but shouldn't be anymore)
- the component is also marked as being mounted and unmounted at the same time.
It's worth noting that this only happens when there are more items than the keep-alive's max
attribute allows to be cached.
Oh. So this sounds a little more serious then. My app is very dependent on being able to go over the max limit and relying on it purging things properly.
@LinusBorg and @grpdream I think this bit me again today in a different way. I'm seeing my watch function getting called for an inactive a disposed child of a keep-alive component. I put onActivated and onDeactivated handlers that set a flag in the component that has the watch and I see the watch getting called for an inactive disposed component. Ugh! I think I'm going to have to write my own composable that wraps watch in order to protect against this. I can write a separate code example of this if necessary, because I'm not 100% sure it's the same bug.
That's actually expected. Components are kept alive - so their reactive defects still works like normal, they are not "asleep" or anything. Just out of the DOM while deactivated.
It's left to you to customize this with the help of the(de)activated hooks
@LinusBorg Sorry, I didn't correctly state this above. This is a component that is no longer in the keep-alive tree. It was removed by maxSize. It doesn't show up as an inactive child in the Vue tools. It's supposed to be gone, yet the watch for it got called.
Okay that sounds different. Also different than your original issue, which is about reactivated components being unresponsive.
It may be related, or not.