Subscribe on changes!

When KeepAlive caches more items than its `max` prop allows, reactivated components may be erroneously marked as being unmounted

avatar
Dec 15th 2022

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.

avatar
Dec 15th 2022

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:

Bildschirm­foto 2022-12-15 um 22 27 55

KeepAlive has 4 children. key=5 is active, key=4 is inactive

after switching to page 4:

Bildschirm­foto 2022-12-15 um 22 29 17
  • 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:

Bildschirm­foto 2022-12-15 um 22 31 11
  • 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.

avatar
Dec 16th 2022

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.

avatar
Jan 16th 2023

@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.

avatar
Jan 16th 2023

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

avatar
Jan 16th 2023

@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.

avatar
Jan 16th 2023

Okay that sounds different. Also different than your original issue, which is about reactivated components being unresponsive.

It may be related, or not.

avatar
Jan 16th 2023

@LinusBorg I'll try to work up an example. Thanks.