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