Setting a reactive object on a shallowReactive map "unwraps" the nested refs in the reactive object
Vue version
@a95e612 (current commit after 3.3.4)
Link to minimal reproduction
Steps to reproduce
Open the example. In the console you see the message printed twice but once you see the internal ref printed.
The issue only shows itself when using "set" on a shallow Reactive map (foo2) If you initialize the map with the same value, it is correct (foo1)
What is expected?
The behavior should be consistent. Either the shallowReactive map should ALWAYS resolves the ref or never.
Imo, the reactive object in the shallowReactive should behave as normal. Hence, in my example, it should always resolve the ref correctly (because thats what the reactive is for)
What is actually happening?
The behavior is inconsistent depending on if the map is initialized with the value or later set
System Info
System:
OS: Linux 5.15 Ubuntu 22.04.1 LTS 22.04.1 LTS (Jammy Jellyfish)
CPU: (16) x64 AMD Ryzen 7 PRO 6850HS with Radeon Graphics
Memory: 10.51 GB / 15.28 GB
Container: Yes
Shell: 5.1.16 - /bin/bash
Binaries:
Node: 19.8.1 - ~/.nvm/versions/node/v19.8.1/bin/node
Yarn: 3.3.0 - /usr/bin/yarn
npm: 8.19.4 - ~/repos/packmatic/node_modules/.bin/npm
Browsers:
Chrome: 114.0.5735.90
Any additional comments?
I am aware that you shouldnt have reactive objects in shallowReactive ones :D
@Fuzzyma @LinusBorg
As per documentation - https://vuejs.org/api/reactivity-advanced.html#shallowreactive
Since they stored are as-is, I tried to log the Map which is passed to shallowReactive as well. The returned values are same ie, as-is (not unwrapped)
const msg = ref('ads')
const myReactive = reactive({msg})
const myMap = new Map([['foo1', myReactive]])
const myReactiveMap = shallowReactive(myMap)
const log = key => console.log(key, myMap.get(key).msg, myReactiveMap.get(key).msg)
log('foo1') // foo1 ads ads
myReactiveMap.set('foo2', myReactive)
log('foo2') // foo2 RefImpl RefImpl
myMap.set('foo3', myReactive)
log('foo3') // foo3 ads ads
It looks like shallowReactive is working as per the documentation.
Also, when I change shallowReactive to reactive, I am getting consistent 'ads' console log for myReactiveMap.get(key).msg
.
In my understanding values being stored "as-is" means they are left untouched.
So when you set as a value on the map with a reactive proxy, that would stay a reactive proxy - and that reactive proxy would unwrap nested refs. just like it does for foo1
, which is stored as the reactive proxy in the map.
The problem here is that shallowReactive seems to apply a toRaw
when setting values, but not when initializing the map with predefined values. Which is an inconsistent behavior.
@shaileshnighojkar I disagree. as-is means, that nothing is changed in whatever object you pass to shallowReactive. Since you pass a reactive object, that exposes every property (and doesnt expose refs), shallowReactive should leave it untouched and just also return every property as before. A reactive object should behave like a transparent proxy. From the shallowReactive functions point of view, it is just a plain object
ref values will not be automatically unwrapped
In a sense the opposite happens because they are going from unwrapped to wrapped
@LinusBorg what do you think which behavior makes more sense?