From e59a6beef42e48897eca6fb63d0ccb38b4c05c8b Mon Sep 17 00:00:00 2001 From: Talha Khan Date: Fri, 16 Oct 2026 18:46:01 +0300 Subject: [PATCH] fix(Fabric, iOS): soften the `_props` subclass assert in RCTViewComponentView.updateProps Third-party Fabric component views that reset `_props` to base ViewProps in `prepareForRecycle` (observed with react-native-maps' `RNMapsGoogleMapView`) can receive `updateProps` with base `ViewProps` on the deep-link mount path (RN 0.88 + new arch). The current RCTAssert aborts the whole app on this diagnostic-only invariant (SIGABRT via NSInternalInconsistencyException), taking down production and dev sessions over a recoverable condition. The static_casts immediately below are to `const ViewProps &` and are valid for base ViewProps, so mounting can continue safely. This changes the assert to an RCTLogError so the condition is still visible in logs while the app survives. Repro: expo-router deep link directly to a screen containing a Google Map while a recycled map view is being re-prepared; app aborts with NSInternalInconsistencyException `...must setup `_props`...` (crash logs show -[RCTViewComponentView updateProps:oldProps:]). Changelog: [iOS] [Fixed] Soften the _props subclass assert in RCTViewComponentView.updateProps to an error log --- .../View/RCTViewComponentView.mm | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index 582557336d61..2c18159bcfb9 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -337,13 +337,21 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & #ifndef NS_BLOCK_ASSERTIONS auto propsRawPtr = _props.get(); - RCTAssert( - propsRawPtr && - ([self class] == [RCTViewComponentView class] || - typeid(*propsRawPtr).hash_code() != typeid(const ViewProps).hash_code()), - @"`RCTViewComponentView` subclasses (and `%@` particularly) must setup `_props`" - " instance variable with a default value in the constructor.", - NSStringFromClass([self class])); + // SOFTENED: third-party component views that reset _props to base ViewProps + // in prepareForRecycle (observed: react-native-maps RNMapsGoogleMapView) can + // receive updateProps with base ViewProps on the deep-link mount path + // (RN 0.88 + new arch). Aborting there takes the whole app down on a + // diagnostic-only invariant, and the static_casts below are valid for base + // ViewProps anyway. Log instead of abort. + if (!(propsRawPtr && + ([self class] == [RCTViewComponentView class] || + typeid(*propsRawPtr).hash_code() != typeid(const ViewProps).hash_code()))) { + RCTLogError( + @"`RCTViewComponentView` subclass `%@` received updateProps with base " + "ViewProps (_props not set by the subclass constructor or recycled). " + "Continuing with base props.", + NSStringFromClass([self class])); + } #endif const auto &oldViewProps = static_cast(*_props);