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;