Subscribe on changes!

watch() on a ref obtained through toRefs() is not a deep watch

avatar
Jul 26th 2023

Vue version

3.3.4

Link to minimal reproduction

https://play.vuejs.org/#eNrtVclu2zAQ/ZWBLnYRR3Yc9KLaAdoghxTogrS3qgdFGklMaFIgKS8w/O8dktqcDUXRAjnUF4nDN8M3o/foffC+qsJ1jUEULAyuKp4YvIgFwOKyZDyDSGGOCkWKyzhIRFpKFQcwHUDmL2AW00FNWupUscrQO1tVUhnYQ4Y5E3gpaS1QmAkoTFLD1ggHyJVcwYjIjd51GZ6V3wmnbmXpP0TMjyHzFhML3DoUnZvU3D2H54/3trG0XeqIKLoKk7buYWKrAGg0dTV+Ay7BpghtwDcPy64LX8/+thHMKNUvdv3i8MbRsq9U8loYVOuEj6ny8qItDk3hcEulu9cTOKNUV2MCZ7PZrK+kiJwSxL0hdHBAS90dt5h2HyKYBEYT+ZwV4Z2WgnTgDo0DOwTGUX2pDKPm4oBm4avTR+Zcbj66mFE1Nq1QTonp/RPxO721sTj4qlBTgxgH3Z5JVIHGb199+4xbeu82VzKrOaFf2LxBLXltOXrYh1pkRHuAc2yvnTqYKL7rq61BodumLNF2OhZNSrFyeK71nu55eO7yaKg0xU6K1kmPdG6BD6VuY+Qc95Tik6zp22dutUlMWvZvV3mOaYv3unILI28w1/ajHlvl90ReKVmRvn+MOveOfh5p2wEeCHxvGXs0GXTZUGigjRo9cqUL54N8PLoWzIx6cXI0hLHdWqfMurjrddwqvjunM82YfHHsCn+W5BhyWbjthoLfckecnAxiRCokWI10cN83pGUiCswiGJGpmrwuy+vC/qZTqAVHrYHmy1nKDN/BKlH3YEr09CHRNG2s7PMWSXYTGGT3MKZBSEPaY0VBJDISwGCyns8g0d6TWHmtWsHZ8BNutzN/3uq0OL7hM7aGlCda07WdWvnGwcW+qXJYTGn7qSvc7DgCXRkVZhQJXaL/JLdJel8oGh9NknpyRFLJpYpgUzKDLlAiK0oT2Qur2rrIhmWmjOBtF8iYpgPpjsw5+kjCWSFOqcSKFJuShJvvc1drw/LdKcmALE1VdZWkeJo4EoSgUdEALGN/1fV/Bv9d+ldc6kqGr8er/8wa8z/2xvyxOQqFKF6dPQ6/AO9LUTE=

Steps to reproduce

In the reproduce setup, the parent component (App.vue) creates a reactive state, called anchor, and passes the state to two child components: Child.vue and Child2.vue.

These two child components are supposed to watch the changes on the anchor property, and update the text whenever anchor state has a change:

  • Child (red) is the one with the bug, the text is not updating unless the watch made explicitly { deep: true }
  • Child2 (green) is the correct one, wired up exactly the same as Child except the stuff being watched is props.reference instead of the reference in const { reference } = toRefs(props);

What is expected?

According to the documentation, this watch in Child.vue should be deep by default, and the text driven by msg should update every second:

   const { reference } = toRefs(props);

    watch(
      reference,
      (val) => {
        counter++;
        msg.value = 'reference changed: ' + counter;
      }
    );

What is actually happening?

The Child.vue watch is not deep, and does not trigger when the parent component updates the anchor state. You have to explicitly make the watch deep in order to get it triggered properly.

System Info

No response

Any additional comments?

No response

avatar
Jul 26th 2023

According to the documentation, this watch in Child.vue should be deep by default

It should not be deep, which part of the documentation are you referring to?

avatar
Jul 26th 2023

I was referring to this: https://vuejs.org/guide/essentials/watchers.html#deep-watchers

But on a second look, it says “if you call watch directly on a reactive object” - does this mean the behavior literally applies only to things created using reactive(), not ref() of an object, or ref whose value is set to a reactive object?

Xinchao

On 26 Jul 2023, at 2:51 PM, Jacek Karczmarczyk @.***> wrote:



According to the documentation, this watch in Child.vue should be deep by default

It should not be deep, which part of the documentation are you referring to?

— Reply to this email directly, view it on GitHubhttps://github.com/vuejs/core/issues/8847#issuecomment-1651081852, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AIUJUJGJDQH62UVOXPV5RTTXSC46LANCNFSM6AAAAAA2YCYQ3U. You are receiving this because you authored the thread.Message ID: @.***>

avatar
Jul 26th 2023

Yes, see https://github.com/vuejs/core/blob/main/packages/runtime-core/src/apiWatch.ts#L208 It may be confusing indeed, if ref's value is an object then the value is a reactive object, probably wording in docs could be improved

avatar
Jul 26th 2023

I see, thanks. Mystery solved then, I will close this.