From ca04723d2ae69cd4ef68f344ca60430854b8e2aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Tue, 14 Nov 2023 02:21:26 -0800 Subject: [PATCH] Guard against unmounted components when using traversal APIs (#41451) Summary: After [this change in React](https://github.com/facebook/react/pull/27687), `ReactFabric.getPublicInstanceFromInternalInstanceHandle` can return `null` if the instance handle is a fiber that was unmounted (before that PR, it would throw an error). This modifies the DOM traversal API to gracefully handle that case. Changelog: [internal] Reviewed By: rshest Differential Revision: D51210455 --- .../Libraries/DOM/Nodes/ReactNativeElement.js | 2 +- .../Libraries/DOM/Nodes/ReadOnlyNode.js | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/react-native/Libraries/DOM/Nodes/ReactNativeElement.js b/packages/react-native/Libraries/DOM/Nodes/ReactNativeElement.js index 2900fe769f7fe7..366a4532a7c939 100644 --- a/packages/react-native/Libraries/DOM/Nodes/ReactNativeElement.js +++ b/packages/react-native/Libraries/DOM/Nodes/ReactNativeElement.js @@ -90,7 +90,7 @@ export default class ReactNativeElement offsetParentInstanceHandle, ); // $FlowExpectedError[incompatible-type] The value returned by `getOffset` is always an instance handle for `ReadOnlyElement`. - const offsetParentElement: ReadOnlyElement = offsetParent; + const offsetParentElement: ReadOnlyElement | null = offsetParent; return offsetParentElement; } } diff --git a/packages/react-native/Libraries/DOM/Nodes/ReadOnlyNode.js b/packages/react-native/Libraries/DOM/Nodes/ReadOnlyNode.js index 8a95751f8e4798..867d93b2cc5cc3 100644 --- a/packages/react-native/Libraries/DOM/Nodes/ReadOnlyNode.js +++ b/packages/react-native/Libraries/DOM/Nodes/ReadOnlyNode.js @@ -134,7 +134,9 @@ export default class ReadOnlyNode { return null; } - return getPublicInstanceFromInternalInstanceHandle(parentInstanceHandle); + return ( + getPublicInstanceFromInternalInstanceHandle(parentInstanceHandle) ?? null + ); } get previousSibling(): ReadOnlyNode | null { @@ -322,9 +324,11 @@ export function getChildNodes( const childNodeInstanceHandles = nullthrows( getFabricUIManager(), ).getChildNodes(shadowNode); - return childNodeInstanceHandles.map(instanceHandle => - getPublicInstanceFromInternalInstanceHandle(instanceHandle), - ); + return childNodeInstanceHandles + .map(instanceHandle => + getPublicInstanceFromInternalInstanceHandle(instanceHandle), + ) + .filter(Boolean); } function getNodeSiblingsAndPosition( @@ -348,7 +352,7 @@ function getNodeSiblingsAndPosition( export function getPublicInstanceFromInternalInstanceHandle( instanceHandle: InternalInstanceHandle, -): ReadOnlyNode { +): ?ReadOnlyNode { const mixedPublicInstance = ReactFabric.getPublicInstanceFromInternalInstanceHandle(instanceHandle); // $FlowExpectedError[incompatible-return] React defines public instances as "mixed" because it can't access the definition from React Native.