vuejs / core

🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
https://vuejs.org/
MIT License
47.65k stars 8.33k forks source link

Replacing an array within a reactive with a copy breaks .indexOf #6035

Open jessevanassen opened 2 years ago

jessevanassen commented 2 years ago

Vue version

3.2.36

Link to minimal reproduction

https://sfc.vuejs.org/#eyJBcHAudnVlIjoiPHNjcmlwdCBzZXR1cD5cbmltcG9ydCB7IHJlYWN0aXZlIH0gZnJvbSAndnVlJztcblxuY29uc3QgaXRlbTEgPSB7IGlkOiAxIH07XG5jb25zdCBpdGVtMiA9IHsgaWQ6IDIgfTtcblxuY29uc3Qgc3RhdGUgPSByZWFjdGl2ZSh7IGl0ZW1zOiBbXSB9KTtcblxuXG5zdGF0ZS5pdGVtcyA9IFsuLi5zdGF0ZS5pdGVtcywgaXRlbTFdO1xuY29uc29sZS5sb2coJ2luZGV4T2YgaXRlbSAxJywgeyBleHBlY3RlZDogMCwgYWN0dWFsOiBzdGF0ZS5pdGVtcy5pbmRleE9mKGl0ZW0xKSB9KTtcblxuc3RhdGUuaXRlbXMgPSBbLi4uc3RhdGUuaXRlbXMsIGl0ZW0yXTtcbmNvbnNvbGUubG9nKCdpbmRleE9mIGl0ZW0gMScsIHsgZXhwZWN0ZWQ6IDAsIGFjdHVhbDogc3RhdGUuaXRlbXMuaW5kZXhPZihpdGVtMSkgfSk7XG5jb25zb2xlLmxvZygnaW5kZXhPZiBpdGVtIDInLCB7IGV4cGVjdGVkOiAxLCBhY3R1YWw6IHN0YXRlLml0ZW1zLmluZGV4T2YoaXRlbTIpIH0pO1xuPC9zY3JpcHQ+IiwiaW1wb3J0LW1hcC5qc29uIjoie1xuICBcImltcG9ydHNcIjoge1xuICAgIFwidnVlXCI6IFwiaHR0cHM6Ly9zZmMudnVlanMub3JnL3Z1ZS5ydW50aW1lLmVzbS1icm93c2VyLmpzXCIsXG4gICAgXCJ2dWUvc2VydmVyLXJlbmRlcmVyXCI6IFwiaHR0cHM6Ly9zZmMudnVlanMub3JnL3NlcnZlci1yZW5kZXJlci5lc20tYnJvd3Nlci5qc1wiXG4gIH1cbn0ifQ==

Steps to reproduce

The expected and actual outcomes are logged to the console

What is expected?

When making a copy of an array within a Reactive and plugging it back into the reactive, I expect that .indexOf will still behave correctly

What is actually happening?

When .slice is called on the array within the reactive, Vue turns it into an Array<Proxy<Object>>, to avoid losing reactivity on the items. When this result is then plugged back into the reactive, those proxied objects remain in the array.

When .indexOf is called with a non-proxied object, it will return -1, as the non-proxied object isn't identity-equal to the proxied one.

createArrayInstumentations already contains a branch for when an operation is called with a proxied argument on an array without proxied objects. My case is the exact opposite: an operation is called with a non-proxied argument on an array with proxied objects. Changing return arr[key](...args.map(toRaw)) to return arr.map(toRaw)[key](...args.map(toRaw)) makes my test case pass.

System Info

No response

Any additional comments?

I ran into this when writing unit tests. I have constant test data, which I made reactive for a test case in a similar way as in the reproduction. Initially, .indexOf worked for searching the array within the reactive, but it failed after changing items in the reactive's array by making a defensive copy.

lidlanca commented 2 years ago

option 1

state.items = [...toRaw(state.items), item2]; 

option 2

state.items.map(toRaw).indexOf(item1)

option 3

don't mix and match raw and reactive playground reactive only