-
-
Notifications
You must be signed in to change notification settings - Fork 8.4k
Commit
Note: shallowRef will always trigger on assignment because it does not account for deep mutations close #1012
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,11 +22,19 @@ describe('reactivity/ref', () => { | |
it('should be reactive', () => { | ||
const a = ref(1) | ||
let dummy | ||
let calls = 0 | ||
effect(() => { | ||
calls++ | ||
dummy = a.value | ||
}) | ||
expect(calls).toBe(1) | ||
expect(dummy).toBe(1) | ||
a.value = 2 | ||
expect(calls).toBe(2) | ||
expect(dummy).toBe(2) | ||
// same value should not trigger | ||
a.value = 2 | ||
expect(calls).toBe(2) | ||
expect(dummy).toBe(2) | ||
}) | ||
|
||
|
@@ -174,6 +182,22 @@ describe('reactivity/ref', () => { | |
expect(dummy).toBe(2) | ||
}) | ||
|
||
test('shallowRef force trigger', () => { | ||
const sref = shallowRef({ a: 1 }) | ||
let dummy | ||
effect(() => { | ||
dummy = sref.value.a | ||
}) | ||
expect(dummy).toBe(1) | ||
|
||
sref.value.a = 2 | ||
expect(dummy).toBe(1) // should not trigger yet | ||
|
||
// force trigger | ||
sref.value = sref.value | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
jods4
Contributor
|
||
expect(dummy).toBe(2) | ||
}) | ||
|
||
test('isRef', () => { | ||
expect(isRef(ref(1))).toBe(true) | ||
expect(isRef(computed(() => 1))).toBe(true) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
import { track, trigger } from './effect' | ||
import { TrackOpTypes, TriggerOpTypes } from './operations' | ||
import { isObject } from '@vue/shared' | ||
import { reactive, isProxy } from './reactive' | ||
import { isObject, hasChanged } from '@vue/shared' | ||
import { reactive, isProxy, toRaw } from './reactive' | ||
import { ComputedRef } from './computed' | ||
import { CollectionTypes } from './collectionHandlers' | ||
|
||
|
@@ -43,27 +43,28 @@ export function shallowRef(value?: unknown) { | |
return createRef(value, true) | ||
} | ||
|
||
function createRef(value: unknown, shallow = false) { | ||
if (isRef(value)) { | ||
return value | ||
} | ||
if (!shallow) { | ||
value = convert(value) | ||
function createRef(rawValue: unknown, shallow = false) { | ||
if (isRef(rawValue)) { | ||
return rawValue | ||
} | ||
let value = shallow ? rawValue : convert(rawValue) | ||
const r = { | ||
_isRef: true, | ||
get value() { | ||
track(r, TrackOpTypes.GET, 'value') | ||
return value | ||
}, | ||
set value(newVal) { | ||
value = shallow ? newVal : convert(newVal) | ||
trigger( | ||
r, | ||
TriggerOpTypes.SET, | ||
'value', | ||
__DEV__ ? { newValue: newVal } : void 0 | ||
) | ||
if (shallow || hasChanged(toRaw(newVal), rawValue)) { | ||
This comment has been minimized.
Sorry, something went wrong.
jods4
Contributor
|
||
rawValue = newVal | ||
value = shallow ? newVal : convert(newVal) | ||
trigger( | ||
r, | ||
TriggerOpTypes.SET, | ||
'value', | ||
__DEV__ ? { newValue: newVal } : void 0 | ||
) | ||
} | ||
} | ||
} | ||
return r | ||
|
3 comments
on commit b0d4df9
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 interesting - does this mean it will do a deep compare? Also is this valid for computed's as well?
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.
@mika76 no, ref
or shallowRef
trigger change when the value they contain changes, they don't do a deep compare.
That being said, ref
is also applying deep reactivity, by wrapping the value it returns with reactive
.
What happens next depend on what you put inside the ref but if it's a plain object it will be made reactive too, thus achieving more or less the same results.
Computed works a bit differently. It's a lazy reactive cache for a computation.
When the dependencies of the computation change, computed will trigger to notify its value has changed, but it won't recompute its value until you actually read it again.
This implies that it can't compare if the computed value has changed when triggering, not even shallowly.
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.
@jods4 ah ok thanks for the explanation
@yyx990803 This is horribly wrong.
shallowRef
should behave likeref
, only shallow.This is unintuitive, inconsistent and counter-performant.
Imagine someone depending on this trigger, then "upgrading" its shallowRef to a full ref and not getting a trigger anymore??
If you want to observe mutations of your object, make your object reactive (either directly, or through a deep
ref
).If you use a
shallowRef
, you explicitely say that you don't care about inner mutations.If you want to use a signal pattern... then create and use an actual signal. With
createRef
it's actually easy now.If we need an API to explicitely trigger a ref that hasn't changed (either deep or shallow), then let's add one. Maybe on the ref:
sref.trigger()
or as I know you don't like that, as a utility function:updateRef(ref)
.