Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions packages/reactivity/__tests__/readonly.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,19 @@ describe('reactivity/readonly', () => {
expect(dummy).toBe(1)
expect(`target is readonly`).toHaveBeenWarnedTimes(2)
})

it('should maintain identity when iterating readonly ref array', () => {
const list = readonly(ref([{}, {}, {}]))
const computedList = computed(() => {
const newList: any[] = []
list.value.forEach(x => newList.push(x))
return newList
})

expect(list.value[0]).toBe(computedList.value[0])
expect(isReadonly(computedList.value[0])).toBe(true)
expect(isReactive(computedList.value[0])).toBe(true)
})
})

const maps = [Map, WeakMap]
Expand Down
54 changes: 45 additions & 9 deletions packages/reactivity/src/arrayInstrumentations.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { TrackOpTypes } from './constants'
import { endBatch, pauseTracking, resetTracking, startBatch } from './effect'
import { isProxy, isShallow, toRaw, toReactive } from './reactive'
import {
isProxy,
isReactive,
isReadonly,
isShallow,
toRaw,
toReactive,
toReadonly,
} from './reactive'
import { ARRAY_ITERATE_KEY, track } from './dep'
import { isArray } from '@vue/shared'

Expand All @@ -24,11 +32,18 @@ export function shallowReadArray<T>(arr: T[]): T[] {
return arr
}

function toWrapped(target: unknown, item: unknown) {
if (isReadonly(target)) {
return isReactive(target) ? toReadonly(toReactive(item)) : toReadonly(item)
}
return toReactive(item)
}

export const arrayInstrumentations: Record<string | symbol, Function> = <any>{
__proto__: null,

[Symbol.iterator]() {
return iterator(this, Symbol.iterator, toReactive)
return iterator(this, Symbol.iterator, item => toWrapped(this, item))
},

concat(...args: unknown[]) {
Expand All @@ -39,7 +54,7 @@ export const arrayInstrumentations: Record<string | symbol, Function> = <any>{

entries() {
return iterator(this, 'entries', (value: [number, unknown]) => {
value[1] = toReactive(value[1])
value[1] = toWrapped(this, value[1])
return value
})
},
Expand All @@ -55,14 +70,28 @@ export const arrayInstrumentations: Record<string | symbol, Function> = <any>{
fn: (item: unknown, index: number, array: unknown[]) => unknown,
thisArg?: unknown,
) {
return apply(this, 'filter', fn, thisArg, v => v.map(toReactive), arguments)
return apply(
this,
'filter',
fn,
thisArg,
v => v.map((item: unknown) => toWrapped(this, item)),
arguments,
)
},

find(
fn: (item: unknown, index: number, array: unknown[]) => boolean,
thisArg?: unknown,
) {
return apply(this, 'find', fn, thisArg, toReactive, arguments)
return apply(
this,
'find',
fn,
thisArg,
item => toWrapped(this, item),
arguments,
)
},

findIndex(
Expand All @@ -76,7 +105,14 @@ export const arrayInstrumentations: Record<string | symbol, Function> = <any>{
fn: (item: unknown, index: number, array: unknown[]) => boolean,
thisArg?: unknown,
) {
return apply(this, 'findLast', fn, thisArg, toReactive, arguments)
return apply(
this,
'findLast',
fn,
thisArg,
item => toWrapped(this, item),
arguments,
)
},

findLastIndex(
Expand Down Expand Up @@ -189,7 +225,7 @@ export const arrayInstrumentations: Record<string | symbol, Function> = <any>{
},

values() {
return iterator(this, 'values', toReactive)
return iterator(this, 'values', item => toWrapped(this, item))
},
}

Expand Down Expand Up @@ -257,7 +293,7 @@ function apply(
if (arr !== self) {
if (needsWrap) {
wrappedFn = function (this: unknown, item, index) {
return fn.call(this, toReactive(item), index, self)
return fn.call(this, toWrapped(self, item), index, self)
}
} else if (fn.length > 2) {
wrappedFn = function (this: unknown, item, index) {
Expand All @@ -281,7 +317,7 @@ function reduce(
if (arr !== self) {
if (!isShallow(self)) {
wrappedFn = function (this: unknown, acc, item, index) {
return fn.call(this, acc, toReactive(item), index, self)
return fn.call(this, acc, toWrapped(self, item), index, self)
}
} else if (fn.length > 3) {
wrappedFn = function (this: unknown, acc, item, index) {
Expand Down
Loading