From 238665dc1d03df118d2ec7fa86469591200cada8 Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Sat, 17 Oct 2026 23:24:35 -0700 Subject: [PATCH] fix(react-native/ios): attach a recycled NativeScriptUIView's handle when it matches its last one Fabric keeps _props across prepareForRecycle while the container is cleared, so diffing against _props skipped setNativeViewHandle when a recycled host was mounted with the handle it last showed, leaving it empty. Diff against the oldProps argument instead, which is null on a fresh mount. --- .../ios/Fabric/NativeScriptUIViewComponentView.mm | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/react-native/ios/Fabric/NativeScriptUIViewComponentView.mm b/packages/react-native/ios/Fabric/NativeScriptUIViewComponentView.mm index f94f048bb..39e07ae9d 100644 --- a/packages/react-native/ios/Fabric/NativeScriptUIViewComponentView.mm +++ b/packages/react-native/ios/Fabric/NativeScriptUIViewComponentView.mm @@ -10,6 +10,11 @@ using namespace facebook::react; +static const std::shared_ptr& NativeScriptUIViewDefaultProps() { + static const auto defaultProps = std::make_shared(); + return defaultProps; +} + static BOOL NativeScriptFabricViewIsDescendantOfView(UIView* view, UIView* ancestor) { UIView* current = view; while (current != nil) { @@ -78,8 +83,7 @@ @implementation NativeScriptUIViewComponentView { - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { - static const auto defaultProps = std::make_shared(); - _props = defaultProps; + _props = NativeScriptUIViewDefaultProps(); _containerView = [[NativeScriptUIView alloc] initWithFrame:self.bounds]; _containerView.hostReadyDelegate = self; @@ -190,7 +194,12 @@ - (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event { } - (void)updateProps:(Props::Shared const&)props oldProps:(Props::Shared const&)oldProps { - const auto oldViewProps = std::static_pointer_cast(_props); + // Diff against oldProps, not _props: React Native leaves _props untouched on + // recycle, while prepareForRecycle clears the container, so a recycled view + // mounted with its previous handle would otherwise never attach it. + const auto oldViewProps = + oldProps ? std::static_pointer_cast(oldProps) + : NativeScriptUIViewDefaultProps(); const auto newViewProps = std::static_pointer_cast(props); const std::string oldNativeViewHandle = oldViewProps->nativeViewHandle; const std::string newNativeViewHandle = newViewProps->nativeViewHandle;