`shallowReactive` collections (Set & Map) incorrectly unproxy added items without re-proxying them on retrieval, leading to identity hazards
Vue version
@9f8e98a
Link to minimal reproduction
Steps to reproduce
Open the console, it should unintuitively log out true false
.
What is expected?
It is expected that the two cases behave the same. Adding items to a collection, then retrieving them again using their iterators (what spread uses) should return the items unchanged.
What is actually happening?
The underlying issue is that, compared to arrays, Sets (and I assume Maps too) always un-proxy items added into them.
This is the expected and correct behavior in the case that the Set is reactive
, and is also what happens with arrays. In this case, when an object is retrieved, it is automatically wrapped into a Proxy again. In fact, the reproduction provided can be fixed by replacing shallowReactive
with reactive
.
However, the bug lies in the fact that when the Set is only shallowReactive
, the unwrapping still happens on add, but it doesn't wrap again on retrieval. This behavior is divergent from arrays, where pushing into a shallowReactive
array simply adds the item unchaged, with the Proxy in tact.
My suggestion: Adding a proxied item into a shallowReactive
collection should leave the item untouched and add the proxied version, making it identical to arrays. If this leads to identity hazards and ends up breaking .has
, then the alternative solution is to always unwrap the item, however to remember to wrap it again on retrieval - regardless if the collection is deeply or shallowly reactive.
System Info
System:
OS: macOS 13.3.1
CPU: (10) arm64 Apple M1 Pro
Memory: 197.44 MB / 16.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 18.14.2 - /opt/homebrew/opt/node@18/bin/node
Yarn: 1.22.19 - /opt/homebrew/bin/yarn
npm: 9.5.0 - /opt/homebrew/opt/node@18/bin/npm
Browsers:
Chrome: 114.0.5735.198
Safari: 16.4
Any additional comments?
I managed to isolate this bug after running into many "identity hazards" while developing my application, as some objects where unexpectedly being unproxied.
I'm not sure if this is a problem.
from the source code. when u use push
to add a value to array. The added value is not changed
https://github.com/vuejs/core/blob/3be4e3cbe34b394096210897c1be8deeb6d748d8/packages/reactivity/src/baseHandlers.ts#L75-L84
(toRaw(this) as any)[key].apply(this, args)
but if u use set to add a value . The added value will be toRaw
https://github.com/vuejs/core/blob/3be4e3cbe34b394096210897c1be8deeb6d748d8/packages/reactivity/src/collectionHandlers.ts#L69-L79
value = toRaw(value)
so they are different.