watchEffect not triggering properly with reactive or ref objects
Vue version
v3.3.4
Link to minimal reproduction
https://stackblitz.com/edit/github-qgjzhk-xwmk5z?file=src%2Fpages%2FHome.vue
Steps to reproduce
In a component within <Suspense>
, I used the following code snippet:
<script setup>
import { ref, reactive, watch, watchEffect } from 'vue';
const stateReact = reactive({ count: 0 });
const stateRef = ref({ count: 0 });
watch(stateReact, () => console.log(stateReact)); // triggers
watch(stateRef, () => console.log(stateRef.value), { deep: true }); // triggers
watchEffect(() => console.log('effect react: ', stateReact)); // do not trigger within suspense
watchEffect(() => console.log('effect ref: ', stateRef.value)); // do not trigger within suspense
const onClick = () => {
stateReact.count++;
stateRef.value.count++;
console.log('clicked');
};
</script>
<template>
<button @click="onClick">test</button>
</template>
Then I open the browser dev tools and look for the logs printed on the console.
What is expected?
I expect 4 logs to be printed to the console each time I click the button. Two for the watch
function and two for the watchEffect
function. That is, I expected the watchEffect
to work just like a watch
after the first render.
What is actually happening?
Only the watch()
functions are triggering when the ref values changes. The watchEffect only triggers on the initial render.
System Info
System:
OS: Linux 5.15 Ubuntu 22.04.2 LTS 22.04.2 LTS (Jammy Jellyfish)
CPU: (4) x64 Intel(R) Core(TM) i5-3470 CPU @ 3.20GHz
Memory: 4.68 GB / 7.74 GB
Container: Yes
Shell: 5.8.1 - /usr/bin/zsh
Binaries:
Node: 18.17.0 - ~/.nvm/versions/node/v18.17.0/bin/node
Yarn: 1.22.19 - ~/.nvm/versions/node/v18.17.0/bin/yarn
npm: 9.6.7 - ~/.nvm/versions/node/v18.17.0/bin/npm
pnpm: 8.9.2 - ~/.nvm/versions/node/v18.17.0/bin/pnpm
Extra: It is windows with wsl2. Brave browser.
Any additional comments?
In my project with nuxt, I noticed that some watchEffects are not triggering. But their equivalent with watch would work just fine. I got help in the vue discord and found that vue alone did not have this problem (playground). Whereas in the nuxt reproduction (stackblitz) the behavior was happening.
With that, I assumed it was a nuxt related issue and opened an issue in the nuxt repository: https://github.com/nuxt/nuxt/issues/23628
They gave a look at it and found out that the same behavior indeed happens with common vue setup, pointing to it being possibly related to suspense with a repro.
That said, in the repro link I sent, I tried to remove suspense, but the behavior seemed to still happen.
Your watchEffects are not equivalent to the watch() variations.
- The First watch watches a reactive object, so a change to any of that objects property will trigger it.
- The second one watches a ref, which by default would only react to a reassignment of the ref's
.value
- but you make it watch all of the nested properties as well with thedeep
option. - Your first watchEffect watches nothing, it just logs the value of the
stateReact
variable. It accesses no reactive properties, so nothing is being watched, an nothing will ever make this effect run again. - Your second watchEffect logs the ref's
.value
, so it will only run if you reassign the ref to a new value. No reactive properties of the nested object are accessed, so changing the nested object'scount
property will not make the effect run again.
I'll move this to discussions since this works as designed and it a usage question / understanding problem.