From cd770e676759f56b5117b9aeb5dce091b51f5cde Mon Sep 17 00:00:00 2001 From: KJ21-ENG Date: Tue, 18 Aug 2026 17:54:45 +0530 Subject: [PATCH] Backport missing ViewState guards for state updates --- patches/react-native/details.md | 8 ++-- ...oft-exception-if-viewState-not-found.patch | 39 ++++++++++++++++++- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/patches/react-native/details.md b/patches/react-native/details.md index 05c68d3d5884..b4ea3c0ab9e6 100644 --- a/patches/react-native/details.md +++ b/patches/react-native/details.md @@ -325,11 +325,11 @@ ### [react-native+0.86.0+038+log-soft-exception-if-viewState-not-found.patch](react-native+0.86.0+038+log-soft-exception-if-viewState-not-found.patch) -- Reason: Restores the Android `updateOverflowInset` half of the dropped `react-native+0.85.3+025+log-soft-exception-if-viewState-not-found.patch`. `SurfaceMountingManager.updateOverflowInset` still resolves its tag through the throwing `getViewState`, so an `INSTRUCTION_UPDATE_OVERFLOW_INSET` op for a view that was already unmounted throws `RetryableMountingLayerException` from inside `IntBufferBatchMountItem.execute`. `MountItemDispatcher.dispatchMountItems` only retries `DispatchCommandMountItem`s, and `RetryableMountingLayerException` is not a `ReactIgnorableMountingException`, so the exception is rethrown and every remaining instruction in that mount transaction is dropped — the incoming views are created but never added or laid out, leaving a blank screen. This patch resolves the tag with `getNullableViewState` and soft-logs + returns instead, matching what upstream already does for `addViewAt`, `updateProps` and `updateLayout`. -- Upstream PR/issue: [#49077](https://github.com/facebook/react-native/issues/49077) [#56762](https://github.com/facebook/react-native/pull/56762) [#7493](https://github.com/software-mansion/react-native-reanimated/issues/7493) +- Reason: Guards Android Fabric's `updateOverflowInset`, `updatePadding`, and `updateState` batch-mount paths against a view tag that was already unmounted. These methods otherwise resolve the tag through the throwing `getViewState`, so a stale batch instruction throws `RetryableMountingLayerException` from inside `IntBufferBatchMountItem.execute`. `MountItemDispatcher.dispatchMountItems` only retries `DispatchCommandMountItem`s, and `IntBufferBatchMountItem` is not retryable, so the exception propagates and crashes the app. The patch uses `getNullableViewState`, soft-logs the missing state, and returns, matching upstream's established handling for stale batch work. The `updateOverflowInset` hunk restores protection dropped during the RN 0.86 upgrade; the `updatePadding` and `updateState` hunks backport upstream commit `0e86a043` after production release `9.4.46-10` confirmed a fatal `getViewState -> updateState -> IntBufferBatchMountItem` recurrence. +- Upstream PR/issue: [#49077](https://github.com/facebook/react-native/issues/49077) [#56762](https://github.com/facebook/react-native/pull/56762) [#57181](https://github.com/facebook/react-native/pull/57181) [#7493](https://github.com/software-mansion/react-native-reanimated/issues/7493) - E/App issues: [#82611](https://github.com/Expensify/App/issues/82611) [#93833](https://github.com/Expensify/App/issues/93833) -- PR introducing patch: [#84303](https://github.com/Expensify/App/pull/84303) (original 0.85.3 patch) -- 0.86.0 migration note: RN 0.86.0 upstreamed the `getNullableViewState` + soft-log guard for `addViewAt`, `updateProps`, `updateLayout` and `removeViewAt`, which is why the 0.85.3 patch was dropped during the upgrade — but it did **not** upstream the `updateOverflowInset` guard, so that one site regressed. Only that site is re-patched here; `updatePadding` and `updateState` still use the throwing `getViewState`, matching 0.85.3 behaviour. Re-check on the RN 0.87 upgrade whether `updateOverflowInset` has been guarded upstream, and drop this patch if so. +- PR introducing patch: [#84303](https://github.com/Expensify/App/pull/84303) (original 0.85.3 patch) and [#98604](https://github.com/Expensify/App/pull/98604) (restored `updateOverflowInset` on 0.86.0) +- 0.86.0 migration note: RN 0.86.0 upstreamed the `getNullableViewState` + soft-log guard for `addViewAt`, `updateProps`, `updateLayout`, and `removeViewAt`, which is why the 0.85.3 patch was dropped during the upgrade, but it did not include the corresponding `updateOverflowInset`, `updatePadding`, or `updateState` guards. All three are patched here. Re-check these methods during the RN 0.87 upgrade and drop this patch once the adopted React Native release contains them upstream. ### [react-native+0.86.0+039+persist-change-bundle-location.patch](react-native+0.86.0+039+persist-change-bundle-location.patch) diff --git a/patches/react-native/react-native+0.86.0+038+log-soft-exception-if-viewState-not-found.patch b/patches/react-native/react-native+0.86.0+038+log-soft-exception-if-viewState-not-found.patch index cf83fe7b0295..42523793e8d7 100644 --- a/patches/react-native/react-native+0.86.0+038+log-soft-exception-if-viewState-not-found.patch +++ b/patches/react-native/react-native+0.86.0+038+log-soft-exception-if-viewState-not-found.patch @@ -1,7 +1,25 @@ diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.kt b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.kt --- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.kt +++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.kt -@@ -899,9 +899,18 @@ internal constructor( +@@ -873,8 +873,15 @@ internal constructor( + if (isStopped) { + return + } +- ++ +- val viewState = getViewState(reactTag) ++ val viewState = getNullableViewState(reactTag) ++ if (viewState == null) { ++ ReactSoftExceptionLogger.logSoftException( ++ ReactSoftExceptionLogger.Categories.SURFACE_MOUNTING_MANAGER_MISSING_VIEWSTATE, ++ ReactNoCrashSoftException("Unable to find viewState for tag $reactTag for updatePadding"), ++ ) ++ return ++ } + // Do not layout Root Views + if (viewState.isRoot) { + return +@@ -899,9 +906,18 @@ internal constructor( if (isStopped) { return } @@ -21,3 +39,22 @@ diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/r if (viewState.isRoot) { return } +@@ -925,8 +941,15 @@ internal constructor( + if (isStopped) { + return + } +- ++ +- val viewState = getViewState(reactTag) +- ++ val viewState = getNullableViewState(reactTag) ++ if (viewState == null) { ++ ReactSoftExceptionLogger.logSoftException( ++ ReactSoftExceptionLogger.Categories.SURFACE_MOUNTING_MANAGER_MISSING_VIEWSTATE, ++ ReactNoCrashSoftException("Unable to find viewState for tag $reactTag for updateState"), ++ ) ++ return ++ } ++ + val prevStateWrapper = viewState.stateWrapper + viewState.stateWrapper = stateWrapper