Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unintuitive Proxy behavior violating set item uniqueness constraint #8647

Open
Vanilagy opened this issue Jun 26, 2023 · 3 comments · May be fixed by #12393
Open

Unintuitive Proxy behavior violating set item uniqueness constraint #8647

Vanilagy opened this issue Jun 26, 2023 · 3 comments · May be fixed by #12393
Labels

Comments

@Vanilagy
Copy link

Vue version

@9f8e98a

Link to minimal reproduction

https://play.vuejs.org/#eNp9U01vozAQ/StTXwJSRA699UtqVzl0D+1qmz2t9+DCENyFMWsbkmyU/96xCbSR2koc8Js34/cew17ctm3WdyguxJXLrW49OPRdeyNJN62xHvZgUeVe9wgHKK1pYMb8mSRJuSHnwVea1nA90ZL9IR1rPOt9hXADT+iT37HnT8o8ST1aXe4YvvfY/CL9r0NC5xLunQ/D00tYLGBVacdnazYutI1I2REPNwRxjsbAUR50CYpA80hgkqpZQ7EDTaCCqDmoogiytYfCoKOZh7xStEbuxsDIhuma7+KbAqjJIxVY8NwCcNti7vnwjJXqtbFg+MLYKOlU0mfWgrYU9pIgiA1YVimXRPiIA9QcoNP/8Q5LY5GzDLQAXA71cGQrQ9dbTK4yXV3Aw+OK7bHgXcyRXTvYILBinCI5xsa2ng3DMbxo9xgdR3aM5FTRbenRngoaCNHMRDi7ZspkIB0+IIRFWFprbCIFxwNxW6ziiIHDrBUneyZFGk0eJPFztRjW80bMhXfML/U6e3GGeHNjVlLkpml1jfaxDeE7KS7GFKVQdW023yPmbYfzEc8rzP9+gL+4bcCk+GHRoe1RiqnmlV1zGrG8fHrALb9PxcYUXc3sL4o/0Zm6CxoH2l1HBct+x4tq7+P/x59t5ZZbXj03mgpCYzCRLwX/jt++sP4m9zw7HwMVh1eqVVu4

Steps to reproduce

Simply open the console, you should see an error.

What is expected?

Sets are expected by the user of the language to behave a certain way, different from arrays. Specifically, they differ in this core aspect:

Pushing an item into an array changes the array, regardless of if the item was already in the array.
Adding an item to a set changes the set if and only if the item was not already in the set.

What is actually happening?

Vue violates this behavior. It seems to get tripped up as the Set is being initilaized with an object already inside it. When .add is called, it first unproxies the object before adding it to the Set. I do not understand this behavior and I think it's counterintuitive. The thing that trips me up in this case is that .has still returns true here. So, .has returns true, yet .add changes the set. Makes no sense.

Either .has should return false here, or, the better solution, .add should not change the set.

System Info

System:
    OS: macOS 13.3.1
    CPU: (10) arm64 Apple M1 Pro
    Memory: 118.56 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.133
    Safari: 16.4

Any additional comments?

Ran into this bug while replacing a bunch of "if not includes, push item" logic by switching to Set.

@Vanilagy Vanilagy changed the title Unintuitive behavior violating set item uniqueness constraint Unintuitive Proxy behavior violating set item uniqueness constraint Jun 26, 2023
@Vanilagy
Copy link
Author

Vanilagy commented Jun 26, 2023

Workarounds for I've found for this are:

  • Initializing with the unproxied elements:
    const set = reactive(new Set(items.map(toRaw)))
  • Manually initializing the Set using a loop:
    const set = reactive(new Set())
    for (const item of items) set.add(item)

@edison1105
Copy link
Member

The cause of the problem is as follows:
The reactive system proxy the set.add, if a reactive object is add, its raw value will be stored. see

if (!shallow && !isShallow(value) && !isReadonly(value)) {

However, during initialization, reactive(new Set([thing])) does not store the raw value of thing, but rather the reactive object.

Perhaps we can handle the initial value of the Set before

const proxy = new Proxy(

If the initial value of the Set is a reactive object, its raw value should be stored. align with set.add. but this may be a breaking change.

Additionally, Map has the same issue. Maybe consider WeakSet and WeakMap as well.

@Vanilagy
Copy link
Author

Good analysis!

I don't know to which degree you can consider the fix to be a breaking change. I consider this to be a bug, so patching the bug may be breaking behavior technically, but it breaks the bug, which is a good thing in my eye.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants
@Vanilagy @edison1105 and others