-
-
Notifications
You must be signed in to change notification settings - Fork 8.4k
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
fix(reactivity): shallowReactive map "unwraps" the nested refs #8503
Conversation
/ecosystem-ci run |
📝 Ran ecosystem CI: Open
|
Size ReportBundles
Usages
|
Shouldn't there be a test for Set as well in that case? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is currently marked as a breaking change. I don't think it is a breaking change, not in the semantic versioning sense.
That said, it could break people's applications if they're relying on the buggy behaviour. I think it'd be appropriate for it to go in a minor
release.
The current behaviour on main
leads to some strange inconsistencies. e.g.:
TS gets confused about what's going on because it's expecting the ref to still be unwrapped for entry c
.
As suggested previously, I think a test should be added to this PR for the Set
case too.
I've also made a suggestion about the specifics of the implementation below.
@@ -66,8 +66,8 @@ function size(target: IterableCollections, isReadonly = false) { | |||
return Reflect.get(target, 'size', target) | |||
} | |||
|
|||
function add(this: SetTypes, value: unknown) { | |||
value = toRaw(value) | |||
function add(this: SetTypes, value: unknown, isShallow = false) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding a boolean flag to a function is generally something that sets off alarm bells for me. There's a lot of stuff been written on this topic, e.g. https://martinfowler.com/bliki/FlagArgument.html, but that isn't to say it's necessarily wrong.
When I first looked over this PR a few weeks ago, I felt like the boolean flag was justified here. But it kept nagging away at me, and I'm now wondering whether there's a better way.
First, I'd like to consider this example:
It's a silly example, but it shows how the flag can be passed accidentally by client code. In that example, the index is being passed by forEach
as the isShallow
value.
Does this matter? Maybe not, but it motivated me to ponder alternative implementations.
I'm now wondering whether this might be a better way to implement it:
-
Remove the call to
toRaw
inadd
andset
. Have them effectively be shallow by default. -
Override the calls to
add
andset
inmutableInstrumentations
to include thetoRaw
call. Something like:add(this: SetTypes, value: unknown) { return add.call(this, toRaw(value)) }, set(this: MapTypes, key: unknown, value: unknown) { return set.call(this, key, toRaw(value)) },
This avoids the need for the boolean flag. I tried this locally and it seemed to pass the test cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Method 1 I don't think is advisable, it will lead to the contamination of the raw data, as shown in the code below, modifying the state through the raw data will cause the effect to be re-executed, and toRaw is used to avoid this
const m = new Map()
const o1 = reactive(m)
const o2 = reactive(new Map())
o1.set('o2', o2)
effect(() => {
console.log(m.get('o2').size)
})
m.get('o2').set('foo', 1)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you misunderstood what I meant. I wasn't proposing two separate methods for fixing the problem, those were two parts of a single method.
I've put together an example to show the changes I had in mind:
The calls to toRaw
are still present, they're just moved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha, I'm sorry, I misunderstood
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think adding the isShallow
arg is fine in this case because it is consistent with how other functions in the same file are structured. The isShallow
flag is used only for value unwrapping in add
and set
, but affects more things in other functions - in general, these handlers have to be aware of the context they are called in.
@skirtles-code @jacekkarczmarczyk thanks for your suggestions. The test case for set has been added. |
/ecosystem-ci run |
📝 Ran ecosystem CI: Open
|
Merging this as this is indeed a correctness fix and passes Ecosystem CI |
[BREAKING CHANGE]
fix #11249
fix #8501
closes #8502
although pr #8502 fix #8501 issue, it is a destructive fix for shallowReactive, so i take a relatively elegant approach to the issue. At the same time, there are also the same issues with Set, so they have been modified together