Skip to content

Commit fa0e198

Browse files
committed
ElemType: preventing a double call to the "mount" lifecycle method (bug fixed)
1 parent ca17c3c commit fa0e198

File tree

4 files changed

+232
-88
lines changed

4 files changed

+232
-88
lines changed

lib/ElemType.js

+17-3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ const descriptors = {
3434
__ref : nullDescriptor,
3535
__handlers : nullDescriptor,
3636
__sampleNode : nullDescriptor,
37+
__mounted : {
38+
writable : true,
39+
value : false,
40+
},
3741
vnode : nullDescriptor,
3842
node : nullDescriptor,
3943
children : nullDescriptor,
@@ -421,11 +425,15 @@ export class ElemType
421425
* @private
422426
*/
423427
__mount() {
428+
if(this.__mounted) {
429+
return
430+
}
424431
let child
425432
for(child of this.children) {
426433
child.props && child.__mount()
427434
}
428435
this.mount()
436+
this.__mounted = true
429437
}
430438

431439
/**
@@ -571,7 +579,9 @@ export class ElemType
571579
else {
572580
node.append(childB.__node || childB.node)
573581
}
574-
childB.__mount()
582+
if(this.__mounted) {
583+
childB.__mount()
584+
}
575585
}
576586
for(i = 0; i < length; i++) {
577587
childB = childrenB[i]
@@ -619,7 +629,9 @@ export class ElemType
619629
childB.__parent = this
620630
childB.__init()
621631
this.node.append(childB.__node || childB.node)
622-
childB.__mount?.()
632+
if(this.__mounted) {
633+
childB.__mount?.()
634+
}
623635
continue
624636
}
625637
if(childB === undefined) {
@@ -651,7 +663,9 @@ export class ElemType
651663
childB.__parent = this
652664
childB.__init()
653665
nodeA.replaceWith(childB.__node || childB.node)
654-
childB.__mount?.()
666+
if(this.__mounted) {
667+
childB.__mount?.()
668+
}
655669
}
656670
}
657671

test/mount-1.test.js

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import test from 'ava'
2+
import sinon from 'sinon'
3+
import { ElemType } from '../index.js'
4+
5+
let mountA, mountB
6+
7+
class ChildA extends ElemType
8+
{
9+
static class = 'ChildA'
10+
11+
mount() {
12+
mountA.apply(this, arguments)
13+
}
14+
}
15+
16+
class ChildB extends ElemType
17+
{
18+
static class = 'ChildB'
19+
20+
mount() {
21+
mountB.apply(this, arguments)
22+
}
23+
}
24+
25+
class Parent extends ElemType
26+
{
27+
static class = 'Parent'
28+
29+
state = {
30+
step : 0,
31+
}
32+
33+
render() {
34+
const props = this.props
35+
switch(this.state.step) {
36+
case 0:
37+
return props.childrenA
38+
case 1:
39+
return new ChildA({ key : props.keyA })
40+
case 2:
41+
return [
42+
new ChildA({ key : props.keyA }),
43+
new ChildB({ key : props.keyB }),
44+
]
45+
}
46+
}
47+
}
48+
49+
test('test #1', t => {
50+
mountA = sinon.spy()
51+
mountB = sinon.spy()
52+
const parent = Parent.render({
53+
childrenA : [],
54+
})
55+
56+
t.is(parent.toString(), '<div class="Parent"></div>')
57+
t.is(mountA.callCount, 0)
58+
t.is(mountB.callCount, 0)
59+
60+
parent.setState({ step : 1 })
61+
62+
t.is(parent.toString(), '<div class="Parent"><div class="ChildA"></div></div>')
63+
t.is(mountA.callCount, 1)
64+
t.is(mountB.callCount, 0)
65+
66+
parent.setState({ step : 2 })
67+
68+
t.is(parent.toString(), '<div class="Parent"><div class="ChildA"></div><div class="ChildB"></div></div>')
69+
t.is(mountA.callCount, 1)
70+
t.is(mountB.callCount, 1)
71+
})
72+
73+
test('test #2', t => {
74+
mountA = sinon.spy()
75+
mountB = sinon.spy()
76+
const parent = Parent.render({
77+
childrenA : undefined,
78+
keyA : 'A',
79+
keyB : 'B',
80+
})
81+
82+
t.is(parent.toString(), '<div class="Parent"></div>')
83+
t.is(mountA.callCount, 0)
84+
t.is(mountB.callCount, 0)
85+
86+
parent.setState({ step : 1 })
87+
88+
t.is(parent.toString(), '<div class="Parent"><div class="ChildA"></div></div>')
89+
t.is(mountA.callCount, 1)
90+
t.is(mountB.callCount, 0)
91+
92+
parent.setState({ step : 2 })
93+
94+
t.is(parent.toString(), '<div class="Parent"><div class="ChildA"></div><div class="ChildB"></div></div>')
95+
t.is(mountA.callCount, 1)
96+
t.is(mountB.callCount, 1)
97+
})

test/mount-2.test.js

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import test from 'ava'
2+
import sinon from 'sinon'
3+
import { ElemType } from '../index.js'
4+
5+
let mountA, mountB
6+
7+
class ChildA extends ElemType
8+
{
9+
static class = 'ChildA'
10+
11+
mount() {
12+
mountA.apply(this, arguments)
13+
}
14+
}
15+
16+
class ChildB extends ElemType
17+
{
18+
static class = 'ChildB'
19+
20+
mount() {
21+
mountB.apply(this, arguments)
22+
}
23+
}
24+
25+
class ParentA extends ElemType
26+
{
27+
static class = 'ParentA'
28+
}
29+
30+
class ParentB extends ElemType
31+
{
32+
static class = 'ParentB'
33+
34+
render() {
35+
const props = this.props
36+
switch(props.step) {
37+
case 1:
38+
return new ChildA({ key : props.keyA })
39+
case 2:
40+
return [
41+
new ChildA({ key : props.keyA }),
42+
new ChildB({ key : props.keyB }),
43+
]
44+
}
45+
}
46+
}
47+
48+
class App extends ElemType
49+
{
50+
static class = 'App'
51+
52+
state = {
53+
step : 0,
54+
}
55+
56+
render() {
57+
const props = this.props
58+
const step = this.state.step
59+
switch(step) {
60+
case 0:
61+
return new ParentA
62+
case 1:
63+
case 2:
64+
return new ParentB({
65+
keyA : props.keyA,
66+
keyB : props.keyB,
67+
step,
68+
})
69+
}
70+
}
71+
}
72+
73+
test('test #1', t => {
74+
mountA = sinon.spy()
75+
mountB = sinon.spy()
76+
const app = App.render()
77+
78+
t.is(app.toString(), '<div class="App"><div class="ParentA"></div></div>')
79+
t.is(mountA.callCount, 0)
80+
t.is(mountB.callCount, 0)
81+
82+
app.setState({ step : 1 })
83+
84+
t.is(app.toString(), '<div class="App"><div class="ParentB"><div class="ChildA"></div></div></div>')
85+
t.is(mountA.callCount, 1)
86+
t.is(mountB.callCount, 0)
87+
88+
app.setState({ step : 2 })
89+
90+
t.is(app.toString(), '<div class="App"><div class="ParentB"><div class="ChildA"></div><div class="ChildB"></div></div></div>')
91+
t.is(mountA.callCount, 1)
92+
t.is(mountB.callCount, 1)
93+
})
94+
95+
test('test #2', t => {
96+
mountA = sinon.spy()
97+
mountB = sinon.spy()
98+
const app = App.render({
99+
keyA : 'A',
100+
keyB : 'B',
101+
})
102+
103+
t.is(app.toString(), '<div class="App"><div class="ParentA"></div></div>')
104+
t.is(mountA.callCount, 0)
105+
t.is(mountB.callCount, 0)
106+
107+
app.setState({ step : 1 })
108+
109+
t.is(app.toString(), '<div class="App"><div class="ParentB"><div class="ChildA"></div></div></div>')
110+
t.is(mountA.callCount, 1)
111+
t.is(mountB.callCount, 0)
112+
113+
app.setState({ step : 2 })
114+
115+
t.is(app.toString(), '<div class="App"><div class="ParentB"><div class="ChildA"></div><div class="ChildB"></div></div></div>')
116+
t.is(mountA.callCount, 1)
117+
t.is(mountB.callCount, 1)
118+
})

test/mount.test.js

-85
This file was deleted.

0 commit comments

Comments
 (0)