findDOMNode: Remove return pointer mutation (#20272)
The last step of the `findDOMNode` algorithm is a search of the current tree. When descending into a child node, it mutates `child.return` so that it points to the current fiber pair, instead of a work-in-progress. This can cause bugs if `findDOMNode` is called at the wrong time, like in an interleaved event. For this reason (among others), you're not suppposed to use `findDOMNode` in Concurrent Mode. However, we still have some internal uses that we haven't migrated. To reduce the potential for bugs, I've removed the `.return` pointer assignment in favor of recursion.
Andrew Clark committed
Nov 16, 2020 at 17:09 UTC
bf7b7aeb1070f5184733ece11e67902d1ace85bd
1 file changed
+37
-51
packages/react-reconciler/src/ReactFiberTreeReflection.js
+37
-51
@@ -265,71 +265,57 @@ export function findCurrentFiberUsingSlowPath(fiber: Fiber): Fiber | null {
265
266
export function findCurrentHostFiber(parent: Fiber): Fiber | null {
267
const currentParent = findCurrentFiberUsingSlowPath(parent);
268
- if (!currentParent) {
269
- return null;
270
- }
268
+ return currentParent !== null
269
+ ? findCurrentHostFiberImpl(currentParent)
270
+ : null;
271
+}
272
273
+function findCurrentHostFiberImpl(node: Fiber) {
274
// Next we'll drill down this component to find the first HostComponent/Text.
273
- let node: Fiber = currentParent;
274
- while (true) {
275
- if (node.tag === HostComponent || node.tag === HostText) {
276
- return node;
277
- } else if (node.child) {
278
- node.child.return = node;
279
- node = node.child;
280
- continue;
281
- }
282
- if (node === currentParent) {
283
- return null;
284
- }
285
- while (!node.sibling) {
286
- if (!node.return || node.return === currentParent) {
287
- return null;
288
- }
289
- node = node.return;
275
+ if (node.tag === HostComponent || node.tag === HostText) {
276
+ return node;
277
+ }
278
+
279
+ let child = node.child;
280
+ while (child !== null) {
281
+ const match = findCurrentHostFiberImpl(child);
282
+ if (match !== null) {
283
+ return match;
284
}
291
- node.sibling.return = node.return;
292
- node = node.sibling;
285
+ child = child.sibling;
286
}
294
- // Flow needs the return null here, but ESLint complains about it.
295
- // eslint-disable-next-line no-unreachable
287
+
288
return null;
289
}
290
291
export function findCurrentHostFiberWithNoPortals(parent: Fiber): Fiber | null {
292
const currentParent = findCurrentFiberUsingSlowPath(parent);
301
- if (!currentParent) {
302
- return null;
303
- }
293
+ return currentParent !== null
294
+ ? findCurrentHostFiberWithNoPortalsImpl(currentParent)
295
+ : null;
296
+}
297
298
+function findCurrentHostFiberWithNoPortalsImpl(node: Fiber) {
299
// Next we'll drill down this component to find the first HostComponent/Text.
306
- let node: Fiber = currentParent;
307
- while (true) {
308
- if (
309
- node.tag === HostComponent ||
310
- node.tag === HostText ||
311
- (enableFundamentalAPI && node.tag === FundamentalComponent)
312
- ) {
313
- return node;
314
- } else if (node.child && node.tag !== HostPortal) {
315
- node.child.return = node;
316
- node = node.child;
317
- continue;
318
- }
319
- if (node === currentParent) {
320
- return null;
321
- }
322
- while (!node.sibling) {
323
- if (!node.return || node.return === currentParent) {
324
- return null;
300
+ if (
301
+ node.tag === HostComponent ||
302
+ node.tag === HostText ||
303
+ (enableFundamentalAPI && node.tag === FundamentalComponent)
304
+ ) {
305
+ return node;
306
+ }
307
+
308
+ let child = node.child;
309
+ while (child !== null) {
310
+ if (child.tag !== HostPortal) {
311
+ const match = findCurrentHostFiberWithNoPortalsImpl(child);
312
+ if (match !== null) {
313
+ return match;
314
}
326
- node = node.return;
315
}
328
- node.sibling.return = node.return;
329
- node = node.sibling;
316
+ child = child.sibling;
317
}
331
- // Flow needs the return null here, but ESLint complains about it.
332
- // eslint-disable-next-line no-unreachable
318
+
319
return null;
320
}
321