Skip to content

Commit 488e2bc

Browse files
authored
fix(reactivity): shallowReactive for collections (#1204)
close #1202
1 parent ba62ccd commit 488e2bc

File tree

3 files changed

+77
-15
lines changed

3 files changed

+77
-15
lines changed

packages/reactivity/__tests__/reactive.spec.ts

+27
Original file line numberDiff line numberDiff line change
@@ -187,5 +187,32 @@ describe('reactivity/reactive', () => {
187187
props.n = reactive({ foo: 2 })
188188
expect(isReactive(props.n)).toBe(true)
189189
})
190+
191+
test('should not observe when iterating', () => {
192+
const shallowSet = shallowReactive(new Set())
193+
const a = {}
194+
shallowSet.add(a)
195+
196+
const spreadA = [...shallowSet][0]
197+
expect(isReactive(spreadA)).toBe(false)
198+
})
199+
200+
test('should not get reactive entry', () => {
201+
const shallowMap = shallowReactive(new Map())
202+
const a = {}
203+
const key = 'a'
204+
205+
shallowMap.set(key, a)
206+
207+
expect(isReactive(shallowMap.get(key))).toBe(false)
208+
})
209+
210+
test('should not get reactive on foreach', () => {
211+
const shallowSet = shallowReactive(new Set())
212+
const a = {}
213+
shallowSet.add(a)
214+
215+
shallowSet.forEach(x => expect(isReactive(x)).toBe(false))
216+
})
190217
})
191218
})

packages/reactivity/src/collectionHandlers.ts

+47-13
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@ const toReactive = <T extends unknown>(value: T): T =>
2222
const toReadonly = <T extends unknown>(value: T): T =>
2323
isObject(value) ? readonly(value) : value
2424

25+
const toShallow = <T extends unknown>(value: T): T => value
26+
2527
const getProto = <T extends CollectionTypes>(v: T): any =>
2628
Reflect.getPrototypeOf(v)
2729

2830
function get(
2931
target: MapTypes,
3032
key: unknown,
31-
wrap: typeof toReactive | typeof toReadonly
33+
wrap: typeof toReactive | typeof toReadonly | typeof toShallow
3234
) {
3335
target = toRaw(target)
3436
const rawKey = toRaw(key)
@@ -132,15 +134,15 @@ function clear(this: IterableCollections) {
132134
return result
133135
}
134136

135-
function createForEach(isReadonly: boolean) {
137+
function createForEach(isReadonly: boolean, shallow: boolean) {
136138
return function forEach(
137139
this: IterableCollections,
138140
callback: Function,
139141
thisArg?: unknown
140142
) {
141143
const observed = this
142144
const target = toRaw(observed)
143-
const wrap = isReadonly ? toReadonly : toReactive
145+
const wrap = isReadonly ? toReadonly : shallow ? toShallow : toReactive
144146
!isReadonly && track(target, TrackOpTypes.ITERATE, ITERATE_KEY)
145147
// important: create sure the callback is
146148
// 1. invoked with the reactive map as `this` and 3rd arg
@@ -152,14 +154,18 @@ function createForEach(isReadonly: boolean) {
152154
}
153155
}
154156

155-
function createIterableMethod(method: string | symbol, isReadonly: boolean) {
157+
function createIterableMethod(
158+
method: string | symbol,
159+
isReadonly: boolean,
160+
shallow: boolean
161+
) {
156162
return function(this: IterableCollections, ...args: unknown[]) {
157163
const target = toRaw(this)
158164
const isMap = target instanceof Map
159165
const isPair = method === 'entries' || (method === Symbol.iterator && isMap)
160166
const isKeyOnly = method === 'keys' && isMap
161167
const innerIterator = getProto(target)[method].apply(target, args)
162-
const wrap = isReadonly ? toReadonly : toReactive
168+
const wrap = isReadonly ? toReadonly : shallow ? toShallow : toReactive
163169
!isReadonly &&
164170
track(
165171
target,
@@ -212,7 +218,22 @@ const mutableInstrumentations: Record<string, Function> = {
212218
set,
213219
delete: deleteEntry,
214220
clear,
215-
forEach: createForEach(false)
221+
forEach: createForEach(false, false)
222+
}
223+
224+
const shallowInstrumentations: Record<string, Function> = {
225+
get(this: MapTypes, key: unknown) {
226+
return get(this, key, toShallow)
227+
},
228+
get size() {
229+
return size((this as unknown) as IterableCollections)
230+
},
231+
has,
232+
add,
233+
set,
234+
delete: deleteEntry,
235+
clear,
236+
forEach: createForEach(false, true)
216237
}
217238

218239
const readonlyInstrumentations: Record<string, Function> = {
@@ -227,25 +248,34 @@ const readonlyInstrumentations: Record<string, Function> = {
227248
set: createReadonlyMethod(TriggerOpTypes.SET),
228249
delete: createReadonlyMethod(TriggerOpTypes.DELETE),
229250
clear: createReadonlyMethod(TriggerOpTypes.CLEAR),
230-
forEach: createForEach(true)
251+
forEach: createForEach(true, false)
231252
}
232253

233254
const iteratorMethods = ['keys', 'values', 'entries', Symbol.iterator]
234255
iteratorMethods.forEach(method => {
235256
mutableInstrumentations[method as string] = createIterableMethod(
236257
method,
258+
false,
237259
false
238260
)
239261
readonlyInstrumentations[method as string] = createIterableMethod(
240262
method,
263+
true,
264+
false
265+
)
266+
shallowInstrumentations[method as string] = createIterableMethod(
267+
method,
268+
true,
241269
true
242270
)
243271
})
244272

245-
function createInstrumentationGetter(isReadonly: boolean) {
246-
const instrumentations = isReadonly
247-
? readonlyInstrumentations
248-
: mutableInstrumentations
273+
function createInstrumentationGetter(isReadonly: boolean, shallow: boolean) {
274+
const instrumentations = shallow
275+
? shallowInstrumentations
276+
: isReadonly
277+
? readonlyInstrumentations
278+
: mutableInstrumentations
249279

250280
return (
251281
target: CollectionTypes,
@@ -271,11 +301,15 @@ function createInstrumentationGetter(isReadonly: boolean) {
271301
}
272302

273303
export const mutableCollectionHandlers: ProxyHandler<CollectionTypes> = {
274-
get: createInstrumentationGetter(false)
304+
get: createInstrumentationGetter(false, false)
305+
}
306+
307+
export const shallowCollectionHandlers: ProxyHandler<CollectionTypes> = {
308+
get: createInstrumentationGetter(false, true)
275309
}
276310

277311
export const readonlyCollectionHandlers: ProxyHandler<CollectionTypes> = {
278-
get: createInstrumentationGetter(true)
312+
get: createInstrumentationGetter(true, false)
279313
}
280314

281315
function checkIdentityKeys(

packages/reactivity/src/reactive.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import {
77
} from './baseHandlers'
88
import {
99
mutableCollectionHandlers,
10-
readonlyCollectionHandlers
10+
readonlyCollectionHandlers,
11+
shallowCollectionHandlers
1112
} from './collectionHandlers'
1213
import { UnwrapRef, Ref } from './ref'
1314

@@ -67,7 +68,7 @@ export function shallowReactive<T extends object>(target: T): T {
6768
target,
6869
false,
6970
shallowReactiveHandlers,
70-
mutableCollectionHandlers
71+
shallowCollectionHandlers
7172
)
7273
}
7374

0 commit comments

Comments
 (0)