@@ -71,60 +71,116 @@ exports.isMounted = function(component : ReactComponent<any, any, any>) : boolea
71
71
return isFiberMounted ( fiber ) === MOUNTED ;
72
72
} ;
73
73
74
- exports . findCurrentHostFiber = function ( parent : Fiber ) : Fiber | null {
75
- // First check if this node itself is mounted.
76
- const state = isFiberMounted ( parent , true ) ;
77
- if ( state === UNMOUNTED ) {
74
+ function assertIsMounted ( fiber ) {
75
+ invariant (
76
+ isFiberMounted ( fiber ) === MOUNTED ,
77
+ 'Unable to find node on an unmounted component.'
78
+ ) ;
79
+ }
80
+
81
+ function findCurrentFiberUsingSlowPath ( fiber : Fiber ) : Fiber | null {
82
+ let alternate = fiber . alternate ;
83
+ if ( ! alternate ) {
84
+ // If there is no alternate, then we only need to check if it is mounted.
85
+ const state = isFiberMounted ( fiber ) ;
78
86
invariant (
79
- false ,
87
+ state !== UNMOUNTED ,
80
88
'Unable to find node on an unmounted component.'
81
89
) ;
82
- } else if ( state === MOUNTING ) {
83
- return null ;
90
+ if ( state === MOUNTING ) {
91
+ return null ;
92
+ }
93
+ return fiber ;
84
94
}
95
+ // If we have two possible branches, we'll walk backwards up to the root
96
+ // to see what path the root points to. On the way we may hit one of the
97
+ // special cases and we'll deal with them.
98
+ let a = fiber ;
99
+ let b = alternate ;
100
+ while ( true ) {
101
+ let parentA = a . return ;
102
+ let parentB = b . return ;
103
+ if ( ! parentA || ! parentB ) {
104
+ // We're at the root.
105
+ break ;
106
+ }
107
+ if ( parentA . child === parentB . child ) {
108
+ // If both parents are the same, then that is the current parent. If
109
+ // they're different but point to the same child, then it doesn't matter.
110
+ // Regardless, whatever child they point to is the current child.
111
+ // So we can now determine which child is current by scanning the child
112
+ // list for either A or B.
113
+ let child = parentA . child ;
114
+ while ( child ) {
115
+ if ( child === a ) {
116
+ // We've determined that A is the current branch.
117
+ assertIsMounted ( parentA ) ;
118
+ return fiber ;
119
+ }
120
+ if ( child === b ) {
121
+ // We've determined that B is the current branch.
122
+ assertIsMounted ( parentA ) ;
123
+ return alternate ;
124
+ }
125
+ child = child . sibling ;
126
+ }
127
+ // We should never have an alternate for any mounting node. So the only
128
+ // way this could possibly happen is if this was unmounted, if at all.
129
+ invariant (
130
+ false ,
131
+ 'Unable to find node on an unmounted component.'
132
+ ) ;
133
+ }
134
+ a = parentA ;
135
+ b = parentB ;
136
+ invariant (
137
+ a . alternate === b ,
138
+ 'Return fibers should always be each others\' alternates.'
139
+ ) ;
140
+ }
141
+ // If the root is not a host container, we're in a disconnected tree. I.e.
142
+ // unmounted.
143
+ invariant (
144
+ a . tag === HostContainer ,
145
+ 'Unable to find node on an unmounted component.'
146
+ ) ;
147
+ if ( a . stateNode . current === a ) {
148
+ // We've determined that A is the current branch.
149
+ return fiber ;
150
+ }
151
+ // Otherwise B has to be current branch.
152
+ return alternate ;
153
+ }
154
+ exports . findCurrentFiberUsingSlowPath = findCurrentFiberUsingSlowPath ;
85
155
86
- let didTryOtherTree = false ;
87
-
88
- // If the component doesn't have a child we first check the alternate to see
89
- // if it has any and if so, if those were just recently inserted.
90
- if ( ! parent . child && parent . alternate ) {
91
- parent = parent . alternate ;
156
+ exports . findCurrentHostFiber = function ( parent : Fiber ) : Fiber | null {
157
+ const currentParent = findCurrentFiberUsingSlowPath ( parent ) ;
158
+ if ( ! currentParent ) {
159
+ return null ;
92
160
}
93
161
94
162
// Next we'll drill down this component to find the first HostComponent/Text.
95
- let node : Fiber = parent ;
163
+ let node : Fiber = currentParent ;
96
164
while ( true ) {
97
- if ( ( node . effectTag & Placement ) !== NoEffect || ! node . return ) {
98
- // If any node along the way was deleted, or is an insertion, that means
99
- // that we're actually in a work in progress to update this component with
100
- // a different component. We need to look in the "current" fiber instead.
101
- if ( ! parent . alternate ) {
102
- return null ;
103
- }
104
- if ( didTryOtherTree ) {
105
- // Safety, to avoid an infinite loop if something goes wrong.
106
- throw new Error ( 'This should never hit this infinite loop.' ) ;
107
- }
108
- didTryOtherTree = true ;
109
- node = parent = parent . alternate ;
110
- continue ;
111
- }
112
165
if ( node . tag === HostComponent || node . tag === HostText ) {
113
166
return node ;
114
167
} else if ( node . child ) {
168
+ // TODO: If we hit a Portal, we're supposed to skip it.
115
169
// TODO: Coroutines need to visit the stateNode.
170
+ node . child . return = node ;
116
171
node = node . child ;
117
172
continue ;
118
173
}
119
- if ( node === parent ) {
174
+ if ( node === currentParent ) {
120
175
return null ;
121
176
}
122
177
while ( ! node . sibling ) {
123
- if ( ! node . return || node . return === parent ) {
178
+ if ( ! node . return || node . return === currentParent ) {
124
179
return null ;
125
180
}
126
181
node = node . return ;
127
182
}
183
+ node . sibling . return = node . return ;
128
184
node = node . sibling ;
129
185
}
130
186
// Flow needs the return null here, but ESLint complains about it.
0 commit comments