1
1
'use strict' ;
2
2
3
- const isEqual = require ( 'lodash.isequal ' ) ;
4
- const { getInstance , getOrCreateInstance } = require ( './instance ' ) ;
3
+ const PropTypes = require ( 'prop-types ' ) ;
4
+ const isEqualShallow = require ( 'is-equal-shallow ' ) ;
5
5
const VNode = require ( './vnode' ) ;
6
6
7
- const diff = ( prevNode , nextNode , onUpdate , context ) => {
8
- const prevInstance = getInstance ( prevNode ) ;
7
+ const isClassComponent = component => component . isComponent ;
8
+
9
+ const checkPropTypes = ( component , props ) => {
10
+ if ( process . env . NODE_ENV !== 'production' && typeof component . propTypes === 'object' ) {
11
+ const name = component . displayName || component . name ;
12
+
13
+ PropTypes . checkPropTypes ( component . propTypes , props , 'prop' , name ) ;
14
+ }
15
+ } ;
16
+
17
+ const getProps = vnode => Object . assign ( { } , vnode . component . defaultProps , vnode . props ) ;
18
+
19
+ const getNextState = vnode => {
20
+ if ( isClassComponent ( vnode . component ) ) {
21
+ return vnode . instance . _pendingState || vnode . instance . state ;
22
+ }
23
+
24
+ return null ;
25
+ } ;
26
+
27
+ const mount = ( vnode , context , onUpdate ) => {
28
+ const props = getProps ( vnode ) ;
29
+ checkPropTypes ( vnode . component , props ) ;
30
+
31
+ if ( isClassComponent ( vnode . component ) ) {
32
+ vnode . createInstance ( props ) ;
33
+ vnode . instance . _onUpdate = onUpdate ;
34
+
35
+ const childContext = vnode . instance . getChildContext ( ) ;
36
+ vnode . instance . context = Object . assign ( { } , context , childContext ) ;
37
+ Object . assign ( context , childContext ) ;
38
+
39
+ vnode . instance . componentWillMount ( ) ;
40
+
41
+ vnode . children = vnode . instance . _render ( ) ;
42
+ } else {
43
+ vnode . children = vnode . component ( props , context ) ;
44
+ }
45
+ } ;
46
+
47
+ const componentDidMount = vnode => {
48
+ if ( isClassComponent ( vnode . component ) ) {
49
+ vnode . instance . componentDidMount ( ) ;
50
+
51
+ if ( vnode . ref ) {
52
+ vnode . ref ( vnode . instance ) ;
53
+ }
54
+ }
55
+ } ;
56
+
57
+ const shouldComponentUpdate = ( vnode , nextProps , nextState ) => {
58
+ if ( isClassComponent ( vnode . component ) ) {
59
+ return vnode . instance . shouldComponentUpdate ( nextProps , nextState ) ;
60
+ }
61
+
62
+ return ! isEqualShallow ( vnode . props , nextProps ) ;
63
+ } ;
64
+
65
+ const componentWillReceiveProps = ( vnode , nextProps ) => {
66
+ vnode . props = nextProps ;
9
67
68
+ if ( isClassComponent ( vnode . component ) ) {
69
+ vnode . instance . componentWillReceiveProps ( nextProps ) ;
70
+ }
71
+ } ;
72
+
73
+ const rerender = vnode => {
74
+ const nextProps = getProps ( vnode ) ;
75
+ checkPropTypes ( vnode . component , nextProps ) ;
76
+
77
+ if ( isClassComponent ( vnode . component ) ) {
78
+ vnode . instance . _componentWillUpdate ( nextProps , getNextState ( vnode ) ) ;
79
+ vnode . children = vnode . instance . _render ( ) ;
80
+ return ;
81
+ }
82
+
83
+ vnode . children = vnode . component ( nextProps , { } ) ;
84
+ } ;
85
+
86
+ const componentDidUpdate = vnode => {
87
+ if ( isClassComponent ( vnode . component ) ) {
88
+ vnode . instance . _componentDidUpdate ( ) ;
89
+ }
90
+ } ;
91
+
92
+ const componentWillUnmount = vnode => {
93
+ if ( isClassComponent ( vnode . component ) ) {
94
+ vnode . instance . componentWillUnmount ( ) ;
95
+ }
96
+ } ;
97
+
98
+ const unmount = vnode => {
99
+ if ( isClassComponent ( vnode . component ) ) {
100
+ componentWillUnmount ( vnode ) ;
101
+ vnode . instance = null ;
102
+ }
103
+
104
+ vnode . children . forEach ( childVNode => {
105
+ diff ( childVNode , null ) ; // eslint-disable-line no-use-before-define
106
+ } ) ;
107
+
108
+ if ( isClassComponent ( vnode . component ) && vnode . ref ) {
109
+ vnode . ref ( null ) ;
110
+ }
111
+ } ;
112
+
113
+ const diff = ( prevNode , nextNode , onUpdate , context ) => {
10
114
if ( typeof nextNode === 'number' ) {
11
115
if ( prevNode instanceof VNode ) {
12
- prevInstance . unmount ( ) ;
116
+ unmount ( prevNode ) ;
13
117
}
14
118
15
119
return String ( nextNode ) ;
16
120
}
17
121
18
122
if ( ! nextNode || typeof nextNode === 'boolean' ) {
19
123
if ( prevNode instanceof VNode ) {
20
- prevInstance . unmount ( ) ;
124
+ unmount ( prevNode ) ;
21
125
}
22
126
23
127
return null ;
24
128
}
25
129
26
130
if ( typeof nextNode === 'string' ) {
27
131
if ( prevNode instanceof VNode ) {
28
- prevInstance . unmount ( ) ;
132
+ unmount ( prevNode ) ;
29
133
}
30
134
31
135
return nextNode ;
32
136
}
33
137
34
- const nextInstance = getOrCreateInstance ( nextNode ) ;
35
-
36
- nextInstance . context = context ;
37
- nextInstance . onUpdate = onUpdate ;
138
+ let isUpdate = true ;
38
139
39
140
if ( ! ( prevNode instanceof VNode ) ) {
40
- nextInstance . mount ( ) ;
41
- return nextNode ;
141
+ mount ( nextNode , context , onUpdate ) ;
142
+ isUpdate = false ;
42
143
}
43
144
44
- if ( prevNode . component !== nextNode . component ) {
45
- prevInstance . unmount ( ) ;
46
- nextInstance . mount ( ) ;
47
- return nextNode ;
145
+ if ( isUpdate && prevNode . component !== nextNode . component ) {
146
+ unmount ( prevNode ) ;
147
+ mount ( nextNode , context , onUpdate ) ;
148
+ isUpdate = false ;
48
149
}
49
150
50
- const shouldUpdate = prevInstance . shouldComponentUpdate ( nextNode . props ) ;
51
- const prevChildren = [ ] . slice . call ( prevNode . children ) ;
151
+ const shouldUpdate = isUpdate && shouldComponentUpdate ( prevNode , getProps ( nextNode ) , getNextState ( prevNode ) ) ;
152
+ const prevChildren = isUpdate ? [ ] . slice . call ( prevNode . children ) : [ ] ;
52
153
53
- if ( ! isEqual ( prevNode . props , nextNode . props ) ) {
54
- prevInstance . props = nextNode . props ;
154
+ if ( isUpdate && ! isEqualShallow ( getProps ( prevNode ) , getProps ( nextNode ) ) ) {
155
+ componentWillReceiveProps ( prevNode , getProps ( nextNode ) ) ;
55
156
}
56
157
57
158
if ( shouldUpdate ) {
58
- prevInstance . rerender ( ) ;
159
+ rerender ( prevNode ) ;
59
160
}
60
161
61
- const nextChildren = prevNode . children ;
162
+ const nextChildren = isUpdate ? prevNode . children : nextNode . children ;
62
163
63
164
const length = Math . max ( prevChildren . length , nextChildren . length ) ;
64
165
const reconciledChildren = [ ] ;
@@ -68,9 +169,18 @@ const diff = (prevNode, nextNode, onUpdate, context) => {
68
169
reconciledChildren . push ( childNode ) ;
69
170
}
70
171
71
- prevInstance . children = reconciledChildren ;
172
+ if ( isUpdate ) {
173
+ prevNode . children = reconciledChildren ;
174
+
175
+ if ( shouldUpdate ) {
176
+ componentDidUpdate ( prevNode ) ;
177
+ }
178
+ } else {
179
+ nextNode . children = reconciledChildren ;
180
+ componentDidMount ( nextNode ) ;
181
+ }
72
182
73
- return prevNode ;
183
+ return isUpdate ? prevNode : nextNode ;
74
184
} ;
75
185
76
186
module . exports = diff ;
0 commit comments