From 68ae8cd5e982db7778a8f9b3aa68494912d75d91 Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Wed, 31 Jan 2024 09:09:36 -0800 Subject: [PATCH] Fix crash in the interop layer when components emits events as soon as are created (#42743) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/42743 When working on Mobile Home, we found a component (RNCSafeAreaView) that was going through the interop layer. The component eits an event as soon as its content view changes, but this is too early: the block that emits the event is still nil at that point in time and that makes the app crash. There might be other components with similarbehavior, therefore, we are fixing it at the interop layer, setting the props immediately after the component is created. ## Changelog: [iOS][Fixed] - Immediately set props of Components that goes through the interop layer Reviewed By: sammy-SC Differential Revision: D53230471 fbshipit-source-id: 90a19e0e87fea381b348b5a7e723ab8b416b828c --- ...CTLegacyViewManagerInteropComponentView.mm | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.mm index b29cffd89bed13..98bcfa8bc48b36 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.mm @@ -210,11 +210,20 @@ - (void)updateState:(const State::Shared &)state oldState:(const State::Shared & - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask { [super finalizeUpdates:updateMask]; + __block BOOL propsUpdated = NO; + + __weak __typeof(self) weakSelf = self; + void (^updatePropsIfNeeded)(RNComponentViewUpdateMask) = ^void(RNComponentViewUpdateMask mask) { + __typeof(self) strongSelf = weakSelf; + if (!propsUpdated) { + [strongSelf _setPropsWithUpdateMask:mask]; + propsUpdated = YES; + } + }; if (!_adapter) { _adapter = [[RCTLegacyViewManagerInteropCoordinatorAdapter alloc] initWithCoordinator:[self _coordinator] reactTag:self.tag]; - __weak __typeof(self) weakSelf = self; _adapter.eventInterceptor = ^(std::string eventName, folly::dynamic event) { if (weakSelf) { __typeof(self) strongSelf = weakSelf; @@ -223,6 +232,13 @@ - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask eventEmitter.dispatchEvent(eventName, event); } }; + // Set props immediately. This is required to set the initial state of the view. + // In the case where some events are fired in relationship of a change in the frame + // or layout of the view, they will fire as soon as the contentView is set and if the + // event block is nil, the app will crash. + updatePropsIfNeeded(updateMask); + propsUpdated = YES; + self.contentView = _adapter.paperView; } @@ -247,6 +263,11 @@ - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask [_adapter.paperView didUpdateReactSubviews]; + updatePropsIfNeeded(updateMask); +} + +- (void)_setPropsWithUpdateMask:(RNComponentViewUpdateMask)updateMask +{ if (updateMask & RNComponentViewUpdateMaskProps) { const auto &newProps = static_cast(*_props); [_adapter setProps:newProps.otherProps];