Skip to content

Commit 66f58ba

Browse files
authoredJun 16, 2020
fix(setup): call stack exceeded when returning circular dependency (#380)
* fix(setup): issue returned from having circular dependency on a ref * chore: refactor tests and handling of ref on unwrapping * chore: code review
1 parent 8c449c1 commit 66f58ba

File tree

2 files changed

+154
-93
lines changed

2 files changed

+154
-93
lines changed
 

‎src/reactivity/unwrap.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,24 @@ import { isRef } from './ref'
22
import { proxy, isFunction, isPlainObject, isArray } from '../utils'
33
import { isReactive } from './reactive'
44

5-
export function unwrapRefProxy(value: any) {
5+
export function unwrapRefProxy(value: any, map = new WeakMap()) {
6+
if (map.has(value)) {
7+
return map.get(value)
8+
}
9+
610
if (
711
isFunction(value) ||
8-
isRef(value) ||
912
isArray(value) ||
1013
isReactive(value) ||
1114
!isPlainObject(value) ||
12-
!Object.isExtensible(value)
15+
!Object.isExtensible(value) ||
16+
isRef(value)
1317
) {
1418
return value
1519
}
1620

1721
const obj: any = {}
22+
map.set(value, obj)
1823

1924
// copy symbols over
2025
Object.getOwnPropertySymbols(value).forEach(
@@ -30,7 +35,7 @@ export function unwrapRefProxy(value: any) {
3035

3136
proxy(obj, k, { get, set })
3237
} else {
33-
obj[k] = unwrapRefProxy(r)
38+
obj[k] = unwrapRefProxy(r, map)
3439
}
3540
}
3641

‎test/setup.spec.js

+145-89
Original file line numberDiff line numberDiff line change
@@ -467,105 +467,161 @@ describe('setup', () => {
467467
.then(done)
468468
})
469469

470-
it('should unwrap on the template', () => {
471-
const vm = new Vue({
472-
setup() {
473-
const r = ref('r')
474-
const nested = {
475-
a: ref('a'),
476-
aa: {
477-
b: ref('aa'),
478-
bb: {
479-
cc: ref('aa'),
480-
c: 'aa',
481-
},
482-
},
470+
describe('setup unwrap', () => {
471+
test('ref', () => {
472+
const vm = new Vue({
473+
setup() {
474+
const r = ref('r')
475+
476+
const refList = ref([ref('1'), ref('2'), ref('3')])
477+
const list = [ref('a'), ref('b')]
478+
479+
return {
480+
r,
481+
refList,
482+
list,
483+
}
484+
},
485+
template: `<div>
486+
<p id="r">{{r}}</p>
487+
<p id="list">{{list}}</p>
488+
<p id="refList">{{refList}}</p>
489+
</div>`,
490+
}).$mount()
491+
492+
expect(vm.$el.querySelector('#r').textContent).toBe('r')
493+
494+
// shouldn't unwrap arrays
495+
expect(
496+
JSON.parse(vm.$el.querySelector('#list').textContent)
497+
).toMatchObject([{ value: 'a' }, { value: 'b' }])
498+
expect(
499+
JSON.parse(vm.$el.querySelector('#refList').textContent)
500+
).toMatchObject([{ value: '1' }, { value: '2' }, { value: '3' }])
501+
})
483502

484-
aaa: reactive({
485-
b: ref('aaa'),
486-
bb: {
487-
c: ref('aaa'),
488-
cc: 'aaa',
503+
test('nested', () => {
504+
const vm = new Vue({
505+
setup() {
506+
const nested = {
507+
a: ref('a'),
508+
aa: {
509+
b: ref('aa'),
510+
bb: {
511+
cc: ref('aa'),
512+
c: 'aa',
513+
},
489514
},
490-
}),
491-
492-
aaaa: {
493-
b: [1],
494-
bb: ref([1]),
495-
bbb: reactive({
496-
c: [1],
497-
cc: ref([1]),
515+
516+
aaa: reactive({
517+
b: ref('aaa'),
518+
bb: {
519+
c: ref('aaa'),
520+
cc: 'aaa',
521+
},
498522
}),
499-
bbbb: [ref(1)],
500-
},
501-
}
502523

503-
const refList = ref([ref('1'), ref('2'), ref('3')])
504-
const list = [ref('a'), ref('b')]
524+
aaaa: {
525+
b: [1],
526+
bb: ref([1]),
527+
bbb: reactive({
528+
c: [1],
529+
cc: ref([1]),
530+
}),
531+
bbbb: [ref(1)],
532+
},
533+
}
505534

506-
return {
507-
r,
508-
nested,
509-
refList,
510-
list,
511-
}
512-
},
513-
template: `<div>
514-
<p id="r">{{r}}</p>
515-
<p id="nested">{{nested.a}}</p>
516-
<p id="list">{{list}}</p>
517-
<p id="refList">{{refList}}</p>
518-
519-
<p id="nested_aa_b">{{ nested.aa.b }}</p>
520-
<p id="nested_aa_bb_c">{{ nested.aa.bb.c }}</p>
521-
<p id="nested_aa_bb_cc">{{ nested.aa.bb.cc }}</p>
522-
523-
<p id="nested_aaa_b">{{ nested.aaa.b }}</p>
524-
<p id="nested_aaa_bb_c">{{ nested.aaa.bb.c }}</p>
525-
<p id="nested_aaa_bb_cc">{{ nested.aaa.bb.cc }}</p>
526-
527-
<p id="nested_aaaa_b">{{ nested.aaaa.b }}</p>
528-
<p id="nested_aaaa_bb_c">{{ nested.aaaa.bb }}</p>
529-
<p id="nested_aaaa_bbb_cc">{{ nested.aaaa.bbb.c }}</p>
530-
<p id="nested_aaaa_bbb_cc">{{ nested.aaaa.bbb.cc }}</p>
531-
<p id="nested_aaaa_bbbb">{{ nested.aaaa.bbbb }}</p>
532-
</div>`,
533-
}).$mount()
535+
return {
536+
nested,
537+
}
538+
},
539+
template: `<div>
540+
<p id="nested">{{nested.a}}</p>
541+
542+
<p id="nested_aa_b">{{ nested.aa.b }}</p>
543+
<p id="nested_aa_bb_c">{{ nested.aa.bb.c }}</p>
544+
<p id="nested_aa_bb_cc">{{ nested.aa.bb.cc }}</p>
545+
546+
<p id="nested_aaa_b">{{ nested.aaa.b }}</p>
547+
<p id="nested_aaa_bb_c">{{ nested.aaa.bb.c }}</p>
548+
<p id="nested_aaa_bb_cc">{{ nested.aaa.bb.cc }}</p>
549+
550+
<p id="nested_aaaa_b">{{ nested.aaaa.b }}</p>
551+
<p id="nested_aaaa_bb_c">{{ nested.aaaa.bb }}</p>
552+
<p id="nested_aaaa_bbb_cc">{{ nested.aaaa.bbb.c }}</p>
553+
<p id="nested_aaaa_bbb_cc">{{ nested.aaaa.bbb.cc }}</p>
554+
<p id="nested_aaaa_bbbb">{{ nested.aaaa.bbbb }}</p>
555+
</div>`,
556+
}).$mount()
534557

535-
expect(vm.$el.querySelector('#r').textContent).toBe('r')
536-
expect(vm.$el.querySelector('#nested').textContent).toBe('a')
558+
expect(vm.$el.querySelector('#nested').textContent).toBe('a')
537559

538-
// shouldn't unwrap arrays
539-
expect(
540-
JSON.parse(vm.$el.querySelector('#list').textContent)
541-
).toMatchObject([{ value: 'a' }, { value: 'b' }])
542-
expect(
543-
JSON.parse(vm.$el.querySelector('#refList').textContent)
544-
).toMatchObject([{ value: '1' }, { value: '2' }, { value: '3' }])
560+
expect(vm.$el.querySelector('#nested_aa_b').textContent).toBe('aa')
561+
expect(vm.$el.querySelector('#nested_aa_bb_c').textContent).toBe('aa')
562+
expect(vm.$el.querySelector('#nested_aa_bb_cc').textContent).toBe('aa')
545563

546-
expect(vm.$el.querySelector('#nested_aa_b').textContent).toBe('aa')
547-
expect(vm.$el.querySelector('#nested_aa_bb_c').textContent).toBe('aa')
548-
expect(vm.$el.querySelector('#nested_aa_bb_cc').textContent).toBe('aa')
564+
expect(vm.$el.querySelector('#nested_aaa_b').textContent).toBe('aaa')
565+
expect(vm.$el.querySelector('#nested_aaa_bb_c').textContent).toBe('aaa')
566+
expect(vm.$el.querySelector('#nested_aaa_bb_cc').textContent).toBe('aaa')
567+
})
549568

550-
expect(vm.$el.querySelector('#nested_aaa_b').textContent).toBe('aaa')
551-
expect(vm.$el.querySelector('#nested_aaa_bb_c').textContent).toBe('aaa')
552-
expect(vm.$el.querySelector('#nested_aaa_bb_cc').textContent).toBe('aaa')
569+
it('recursive', () => {
570+
const vm = new Vue({
571+
setup() {
572+
const b = {
573+
c: 'c',
574+
}
553575

554-
expect(
555-
JSON.parse(vm.$el.querySelector('#nested_aaaa_b').textContent)
556-
).toMatchObject([1])
557-
expect(
558-
JSON.parse(vm.$el.querySelector('#nested_aaaa_bb_c').textContent)
559-
).toMatchObject([1])
560-
expect(
561-
JSON.parse(vm.$el.querySelector('#nested_aaaa_bbb_cc').textContent)
562-
).toMatchObject([1])
563-
expect(
564-
JSON.parse(vm.$el.querySelector('#nested_aaaa_bbb_cc').textContent)
565-
).toMatchObject([1])
566-
expect(
567-
JSON.parse(vm.$el.querySelector('#nested_aaaa_bbbb').textContent)
568-
).toMatchObject([{ value: 1 }])
576+
const recursive = {
577+
a: {
578+
a: 'a',
579+
b,
580+
},
581+
}
582+
583+
b.recursive = recursive
584+
b.r = ref('r')
585+
586+
return {
587+
recursive,
588+
}
589+
},
590+
template: `<div>
591+
<p id="recursive_a">{{recursive.a.a}}</p>
592+
<p id="recursive_b_c">{{recursive.a.b.c}}</p>
593+
<p id="recursive_b_r">{{recursive.a.b.r}}</p>
594+
595+
<p id="recursive_b_recursive_a">{{recursive.a.b.recursive.a.a}}</p>
596+
<p id="recursive_b_recursive_c">{{recursive.a.b.recursive.a.b.c}}</p>
597+
<p id="recursive_b_recursive_r">{{recursive.a.b.recursive.a.b.r}}</p>
598+
599+
<p id="recursive_b_recursive_recursive_c">{{recursive.a.b.recursive.a.b.recursive.a.b.c}}</p>
600+
<p id="recursive_b_recursive_recursive_r">{{recursive.a.b.recursive.a.b.recursive.a.b.r}}</p>
601+
</div>`,
602+
}).$mount()
603+
expect(vm.$el.querySelector('#recursive_a').textContent).toBe('a')
604+
expect(vm.$el.querySelector('#recursive_b_c').textContent).toBe('c')
605+
expect(vm.$el.querySelector('#recursive_b_r').textContent).toBe('r')
606+
607+
expect(vm.$el.querySelector('#recursive_b_recursive_a').textContent).toBe(
608+
'a'
609+
)
610+
expect(vm.$el.querySelector('#recursive_b_recursive_c').textContent).toBe(
611+
'c'
612+
)
613+
expect(vm.$el.querySelector('#recursive_b_recursive_r').textContent).toBe(
614+
'r'
615+
)
616+
617+
expect(
618+
vm.$el.querySelector('#recursive_b_recursive_recursive_c').textContent
619+
).toBe('c')
620+
621+
expect(
622+
vm.$el.querySelector('#recursive_b_recursive_recursive_r').textContent
623+
).toBe('r')
624+
})
569625
})
570626

571627
it('should not unwrap built-in objects on the template', () => {

0 commit comments

Comments
 (0)
Please sign in to comment.