Subscribe on changes!

Watcher doesn't trigger for globally declared object refs exported via composable

avatar
Jun 28th 2023

Vue version

3.3.4

Link to minimal reproduction

https://play.vuejs.org/#eNqNVVtv2jAU/iuWNQmqorCpPCGItlXVtGnapsG0lzw0BBPSOnbkC0VC+e89viRxoAVAKPhcv+NzzpcD/lJV0U4TPMUzmYmiUkgSpas4YUVZcaHQAWlJ7jkcZLqiBNVoI3iJBtG4J4+UHAQ+L6nKtiMkyKZ1gCxgkbCMM2lMcspXKV2oVJER0tUanv6Qcc0UEeA57ycf3nQBvNF/k4iIpSjynAiyXhYlkeAHmYcfwdwZB7muc6A8u9L+1MMrDwlDTrokUk0R05QmrLYuG80yVXDm6/7Z+g5vUOf3fv5ol1JNbm/7pk4atTkByOOCl8SZIKtFHw5Xxa4fAauBajs59Lc9QgBwHjuM5zrQAXQFuyi9lgeRLrXnOJr5zsZuXmFS4aBIWVFwhxNCs+2n2NZv3abo0Bu2SBlVXc/GYGbMncskvvdjZ7HCUzUYkDIgbJyzU2djTjyESfzN5kTSzsS5oBensw1sQ6+0UjA6nzNaZM/zBAe7k+D4nz25rLOxs208oWA7asheQXc7wQh1wxPckKnGOV4u5tLmXFNLtw9dQW6GT8syk9B2H4+wkrCPmyKPniRnQGt2wBKcAYkUlIjfldk7mWAAazRGl1LKX35YmRKajBo5oM+e35A/yb2RJfiPIJKIHaBsdSoVOVFO/bD4Rfbwv1WWfK0pWJ9R/iWSU20wOrOvmq0BdmBn0X63RFuwfCkf9oow2RRlgBrL2tonGFjX8Od7pXdw76I76wc7Brd4zO1wkS23n5C6gC74X4+bO5ZsNcGotzQJSgPGzKRnSZDAovuQZG/zdpTZfyNALT7AEal2fOr15uORtYQSqEKKcERqt6Th0IY9exGAJXvBGTTP7FA+HNixhQYhtfXbOBid5rBVGt+6KdccBLyCBesjD7kzlIdvzjcq7cWvcf0Kgn3Lqw==

Steps to reproduce

  1. Create composable function
  2. Declare object ref in higher scope
  3. Export it via composable function
  4. Try to 'watch' the value

What is expected?

I expect my watcher to trigger every time when ref changes

What is actually happening?

Watcher doesn't trigger despite the ref changing

System Info

No response

Any additional comments?

I need to share reactive state in a composable to use it between components. I know that this might sound stupid and most of you would recommend using Pinia, which would easily solve my problem, but I can't because I am working on a library and I would like to avoid extra dependencies (also adding Pinia as a dependency to a package is a pain in butt). I think my approach would solve my problem fine, but it looks like a bug with watch(). You can see in the repro that it works fine with primitive type declared globally, but not with the object. However, local object watcher works fine also.

Also, if this would work, I would question the purpose of Pinia at all because it looks like a native alternative without any packages.

avatar
Jun 28th 2023

Crap, the solution was simple: image => image

But it seems it depends the way I declare my watcher depends on the way I update the ref.

function updateState() {
    globalState.value = { test: `Some value ${counter.value}` } // assigning to the whole ref
}

watch(globalState, () => { // will trigger
  globalStateWatcherTriggeredTimes.value++
})

watch(globalState.value, () => { // will NOT trigger
  globalStateWatcherTriggeredTimes.value++
})
function updateState() {
    globalState.value.test = `Some value ${counter.value}` // assigning to the property
}

watch(globalState, () => { // will NOT trigger
  globalStateWatcherTriggeredTimes.value++
})

watch(globalState.value, () => { // WILL trigger
  globalStateWatcherTriggeredTimes.value++
})

Maybe it's just my misunderstanding of how watch() works.

avatar
Jun 28th 2023

Also, locally my problem was that I've tried to put in that global ref the return value of window.getSelection() and ironically it returns the same object every time (window.getSelection() === window.getSelection()), which I didn't consider initially, so my solution was to just globalState.value = { ...window.getSelection() }