Subscribe on changes!

Setting a reactive object on a shallowReactive map "unwraps" the nested refs in the reactive object

avatar
Jun 5th 2023

Vue version

@a95e612 (current commit after 3.3.4)

Link to minimal reproduction

https://play.vuejs.org/#eNqNkE1ugzAQha8y8sZGoiCyrEikHqCbbuMsEDEEyX/ChqiyfPeOCaJNk0U2lmfe957HE8iHtcU8CfJOateOg/XghJ/sgetBWTN6CDCKLsejaf0wixzcpZHSXL/WBkToRqOAYgrlGoDr1mjnQbke9snMaHN2NNv635s1ybcrC4jHJ8xnYxH79ybT4gqosOORdsZUNP/jOJ0yzOH6LqPAX7HE7u7YBUxPGikKaXr2KxU40DrPo5gS+zWxotnL7G5j6/K2blw0Fl4oKxsvsAKoL9UhhGV/MdYlVkt30HbyML8pcxZyzwnqnKBUl5sbSPwBWo+lvA==

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

avatar
Jun 5th 2023

@Fuzzyma @LinusBorg

As per documentation - https://vuejs.org/api/reactivity-advanced.html#shallowreactive

Screenshot



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

Link: https://play.vuejs.org/#eNp1kUEOgjAQRa8yYdOaEIy6M2riAdy4tS4IFiRC29ACIYS7OwNKgOimaf+8P/PTab2zMUFVSm/vHWxUpMaBla40J6HS3OjCQQuFjH08wsillfTBPsMs0/X1I0AHcaFzYNiFCSVUpJV1kNsEjmTlLHxYthr1ZjRSebjyFvFuwlxCg2Ula8Abv91YrPWG+RP3/f6j5WBbBOR9u5HONAV7yQaOJyBJZzJAkaNEA5ANEunouQow1nTosoZNhSLvkK9/zmn8zL64nYVH8OvakguAfFN+94/fEX9YD7vCLQnldW+FkZ/Y

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.

avatar
Jun 5th 2023

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.

avatar
Jun 6th 2023

@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?

avatar
Jun 6th 2023

I think my previous reply is pretty much in sync with your understanding