From 314cfa8c9108581e027b71139c005ab3385acf1e Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 16 Jan 2024 09:31:34 -0800 Subject: [PATCH] Send Modal onDismiss event on iOS (Fabric) and Android (#42014) Summary: 1. Modal onDismiss is not working on iOS (Fabric). 2. Modal onDismiss is currently only available on iOS. On Android, we don't have a way to know when exactly a modal is dismissed. Currently, the onDismiss is emitted using a device event as a workaround to the RCTModalHostView unable to receive the component event as it's already unmounted when visible is false. This PR removes the workaround and keeps RCTModalHostView mounted until the onDismiss event is emitted from the host and sends the onDismiss event on Android. bypass-github-export-checks ## Changelog: [ANDROID] [ADDED] - Added support for Modal onDismiss prop [IOS] [FIXED] - Fix onDismiss is not working on Fabric [General][Breaking] - The public API of Modal has changed. We don't have anymore a NativeModalManger turbomodule; RCTModalHostViewNtiveComponent's Prop does not require to pass an identifier anymore. Pull Request resolved: https://github.com/facebook/react-native/pull/42014 Test Plan: 1. Run rn-tester 2. Open the Modal example 3. The second example shows the counter for the show and dismiss count 4. Show and dismiss the modal and verify the count is incremented correctly https://github.com/facebook/react-native/assets/50919443/108bfb26-c8f6-43b2-ac40-f0b46e48771b Reviewed By: javache, sammy-SC Differential Revision: D52445670 Pulled By: cipolleschi fbshipit-source-id: f419164032c3bef67387200778b274299bf0659f --- .../react-native/Libraries/Modal/Modal.d.ts | 9 ++- .../react-native/Libraries/Modal/Modal.js | 70 +++++-------------- .../Libraries/Modal/NativeModalManager.js | 21 ------ .../Modal/RCTModalHostViewNativeComponent.js | 11 +-- .../__snapshots__/Modal-test.js.snap | 1 - .../__snapshots__/public-api-test.js.snap | 10 --- .../Modal/RCTModalHostViewComponentView.mm | 1 - .../React/Views/RCTModalHostView.h | 6 +- .../React/Views/RCTModalHostViewManager.m | 10 +-- .../React/Views/RCTModalManager.h | 17 ----- .../React/Views/RCTModalManager.m | 42 ----------- .../react/views/modal/DismissEvent.java | 40 +++++++++++ .../views/modal/ReactModalHostManager.java | 17 +++-- .../react/views/modal/ReactModalHostView.java | 19 +++++ .../js/examples/Modal/ModalOnShow.js | 2 +- .../js/examples/Modal/ModalPresentation.js | 4 +- 16 files changed, 98 insertions(+), 182 deletions(-) delete mode 100644 packages/react-native/Libraries/Modal/NativeModalManager.js delete mode 100644 packages/react-native/React/Views/RCTModalManager.h delete mode 100644 packages/react-native/React/Views/RCTModalManager.m create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/DismissEvent.java diff --git a/packages/react-native/Libraries/Modal/Modal.d.ts b/packages/react-native/Libraries/Modal/Modal.d.ts index 4cc2df22367b95..1b035876efd994 100644 --- a/packages/react-native/Libraries/Modal/Modal.d.ts +++ b/packages/react-native/Libraries/Modal/Modal.d.ts @@ -43,6 +43,10 @@ export interface ModalBaseProps { * The `onShow` prop allows passing a function that will be called once the modal has been shown. */ onShow?: ((event: NativeSyntheticEvent) => void) | undefined; + /** + * The `onDismiss` prop allows passing a function that will be called once the modal has been dismissed. + */ + onDismiss?: (() => void) | undefined; } export interface ModalPropsIOS { @@ -70,11 +74,6 @@ export interface ModalPropsIOS { > | undefined; - /** - * The `onDismiss` prop allows passing a function that will be called once the modal has been dismissed. - */ - onDismiss?: (() => void) | undefined; - /** * The `onOrientationChange` callback is called when the orientation changes while the modal is being displayed. * The orientation provided is only 'portrait' or 'landscape'. This callback is also called on initial render, regardless of the current orientation. diff --git a/packages/react-native/Libraries/Modal/Modal.js b/packages/react-native/Libraries/Modal/Modal.js index 9750d2e5be31d3..44e70a73a61601 100644 --- a/packages/react-native/Libraries/Modal/Modal.js +++ b/packages/react-native/Libraries/Modal/Modal.js @@ -12,10 +12,7 @@ import type {ViewProps} from '../Components/View/ViewPropTypes'; import type {RootTag} from '../ReactNative/RootTag'; import type {DirectEventHandler} from '../Types/CodegenTypes'; -import NativeEventEmitter from '../EventEmitter/NativeEventEmitter'; -import {type EventSubscription} from '../vendor/emitter/EventEmitter'; import ModalInjection from './ModalInjection'; -import NativeModalManager from './NativeModalManager'; import RCTModalHostView from './RCTModalHostViewNativeComponent'; import {VirtualizedListContextResetter} from '@react-native/virtualized-lists'; @@ -25,34 +22,14 @@ const AppContainer = require('../ReactNative/AppContainer'); const I18nManager = require('../ReactNative/I18nManager'); const {RootTagContext} = require('../ReactNative/RootTag'); const StyleSheet = require('../StyleSheet/StyleSheet'); -const Platform = require('../Utilities/Platform'); const React = require('react'); -type ModalEventDefinitions = { - modalDismissed: [{modalID: number}], -}; - -const ModalEventEmitter = - Platform.OS === 'ios' && NativeModalManager != null - ? new NativeEventEmitter( - // T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior - // If you want to use the native module on other platforms, please remove this condition and test its behavior - Platform.OS !== 'ios' ? null : NativeModalManager, - ) - : null; - /** * The Modal component is a simple way to present content above an enclosing view. * * See https://reactnative.dev/docs/modal */ -// In order to route onDismiss callbacks, we need to uniquely identifier each -// on screen. There can be different ones, either nested or as siblings. -// We cannot pass the onDismiss callback to native as the view will be -// destroyed before the callback is fired. -let uniqueModalIdentifier = 0; - type OrientationChangeEvent = $ReadOnly<{| orientation: 'portrait' | 'landscape', |}>; @@ -159,6 +136,10 @@ export type Props = $ReadOnly<{| onOrientationChange?: ?DirectEventHandler, |}>; +type State = {| + isRendering: boolean, +|}; + function confirmProps(props: Props) { if (__DEV__) { if ( @@ -173,7 +154,7 @@ function confirmProps(props: Props) { } } -class Modal extends React.Component { +class Modal extends React.Component { static defaultProps: {|hardwareAccelerated: boolean, visible: boolean|} = { visible: true, hardwareAccelerated: false, @@ -181,45 +162,27 @@ class Modal extends React.Component { static contextType: React.Context = RootTagContext; - _identifier: number; - _eventSubscription: ?EventSubscription; - constructor(props: Props) { super(props); + this.state = { + isRendering: props.visible === true, + }; if (__DEV__) { confirmProps(props); } - this._identifier = uniqueModalIdentifier++; } - componentDidMount() { - // 'modalDismissed' is for the old renderer in iOS only - if (ModalEventEmitter) { - this._eventSubscription = ModalEventEmitter.addListener( - 'modalDismissed', - event => { - if (event.modalID === this._identifier && this.props.onDismiss) { - this.props.onDismiss(); - } - }, - ); - } - } - - componentWillUnmount() { - if (this._eventSubscription) { - this._eventSubscription.remove(); + componentDidUpdate(prevProps: Props) { + if (prevProps.visible !== true && this.props.visible === true) { + this.setState({isRendering: true}); } - } - - componentDidUpdate() { if (__DEV__) { confirmProps(this.props); } } render(): React.Node { - if (this.props.visible !== true) { + if (this.props.visible !== true && !this.state.isRendering) { return null; } @@ -253,13 +216,14 @@ class Modal extends React.Component { onRequestClose={this.props.onRequestClose} onShow={this.props.onShow} onDismiss={() => { - if (this.props.onDismiss) { - this.props.onDismiss(); - } + this.setState({isRendering: false}, () => { + if (this.props.onDismiss) { + this.props.onDismiss(); + } + }); }} visible={this.props.visible} statusBarTranslucent={this.props.statusBarTranslucent} - identifier={this._identifier} style={styles.modal} // $FlowFixMe[method-unbinding] added when improving typing for this parameters onStartShouldSetResponder={this._shouldSetResponder} diff --git a/packages/react-native/Libraries/Modal/NativeModalManager.js b/packages/react-native/Libraries/Modal/NativeModalManager.js deleted file mode 100644 index f85a77ac4b5ce7..00000000000000 --- a/packages/react-native/Libraries/Modal/NativeModalManager.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow strict - * @format - */ - -import type {TurboModule} from '../TurboModule/RCTExport'; - -import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; - -export interface Spec extends TurboModule { - // RCTEventEmitter - +addListener: (eventName: string) => void; - +removeListeners: (count: number) => void; -} - -export default (TurboModuleRegistry.get('ModalManager'): ?Spec); diff --git a/packages/react-native/Libraries/Modal/RCTModalHostViewNativeComponent.js b/packages/react-native/Libraries/Modal/RCTModalHostViewNativeComponent.js index a21af54ae4fe72..ea80fa8ff5bb1d 100644 --- a/packages/react-native/Libraries/Modal/RCTModalHostViewNativeComponent.js +++ b/packages/react-native/Libraries/Modal/RCTModalHostViewNativeComponent.js @@ -10,11 +10,7 @@ import type {ViewProps} from '../Components/View/ViewPropTypes'; import type {HostComponent} from '../Renderer/shims/ReactNativeTypes'; -import type { - DirectEventHandler, - Int32, - WithDefault, -} from '../Types/CodegenTypes'; +import type {DirectEventHandler, WithDefault} from '../Types/CodegenTypes'; import codegenNativeComponent from '../Utilities/codegenNativeComponent'; @@ -126,11 +122,6 @@ type NativeProps = $ReadOnly<{| * See https://reactnative.dev/docs/modal#onorientationchange */ onOrientationChange?: ?DirectEventHandler, - - /** - * The `identifier` is the unique number for identifying Modal components. - */ - identifier?: WithDefault, |}>; export default (codegenNativeComponent('ModalHostView', { diff --git a/packages/react-native/Libraries/Modal/__tests__/__snapshots__/Modal-test.js.snap b/packages/react-native/Libraries/Modal/__tests__/__snapshots__/Modal-test.js.snap index 5c2f5c57f07640..9a98b1d43c8050 100644 --- a/packages/react-native/Libraries/Modal/__tests__/__snapshots__/Modal-test.js.snap +++ b/packages/react-native/Libraries/Modal/__tests__/__snapshots__/Modal-test.js.snap @@ -13,7 +13,6 @@ exports[` should render as when not mocked 1`] = ` void; - +removeListeners: (count: number) => void; -} -declare export default ?Spec; -" -`; - exports[`public API should not change unintentionally Libraries/Modal/RCTModalHostViewNativeComponent.js 1`] = ` "type OrientationChangeEvent = $ReadOnly<{| orientation: \\"portrait\\" | \\"landscape\\", @@ -6488,7 +6479,6 @@ type NativeProps = $ReadOnly<{| \\"portrait\\", >, onOrientationChange?: ?DirectEventHandler, - identifier?: WithDefault, |}>; declare export default HostComponent; " diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/Modal/RCTModalHostViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/Modal/RCTModalHostViewComponentView.mm index e20eee608a01c6..d814fa0b91d752 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/Modal/RCTModalHostViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/Modal/RCTModalHostViewComponentView.mm @@ -8,7 +8,6 @@ #import "RCTModalHostViewComponentView.h" #import -#import #import #import #import diff --git a/packages/react-native/React/Views/RCTModalHostView.h b/packages/react-native/React/Views/RCTModalHostView.h index 2fcdcaea83f5b2..67f6de08b3596d 100644 --- a/packages/react-native/React/Views/RCTModalHostView.h +++ b/packages/react-native/React/Views/RCTModalHostView.h @@ -23,6 +23,7 @@ @property (nonatomic, assign, getter=isTransparent) BOOL transparent; @property (nonatomic, copy) RCTDirectEventBlock onShow; +@property (nonatomic, copy) RCTDirectEventBlock onDismiss; @property (nonatomic, assign) BOOL visible; // Android only @@ -30,16 +31,11 @@ @property (nonatomic, assign) BOOL hardwareAccelerated; @property (nonatomic, assign) BOOL animated; -@property (nonatomic, copy) NSNumber *identifier; - @property (nonatomic, weak) id delegate; @property (nonatomic, copy) NSArray *supportedOrientations; @property (nonatomic, copy) RCTDirectEventBlock onOrientationChange; -// Fabric only -@property (nonatomic, copy) RCTDirectEventBlock onDismiss; - - (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER; @end diff --git a/packages/react-native/React/Views/RCTModalHostViewManager.m b/packages/react-native/React/Views/RCTModalHostViewManager.m index 4b9f9ad7267c8f..c3ebd189f1d073 100644 --- a/packages/react-native/React/Views/RCTModalHostViewManager.m +++ b/packages/react-native/React/Views/RCTModalHostViewManager.m @@ -10,7 +10,6 @@ #import "RCTBridge.h" #import "RCTModalHostView.h" #import "RCTModalHostViewController.h" -#import "RCTModalManager.h" #import "RCTShadowView.h" #import "RCTUtils.h" @@ -91,8 +90,8 @@ - (void)dismissModalHostView:(RCTModalHostView *)modalHostView animated:(BOOL)animated { dispatch_block_t completionBlock = ^{ - if (modalHostView.identifier) { - [[self.bridge moduleForClass:[RCTModalManager class]] modalDismissed:modalHostView.identifier]; + if (modalHostView.onDismiss) { + modalHostView.onDismiss(nil); } }; dispatch_async(dispatch_get_main_queue(), ^{ @@ -124,13 +123,10 @@ - (void)invalidate RCT_EXPORT_VIEW_PROPERTY(hardwareAccelerated, BOOL) RCT_EXPORT_VIEW_PROPERTY(animated, BOOL) RCT_EXPORT_VIEW_PROPERTY(onShow, RCTDirectEventBlock) -RCT_EXPORT_VIEW_PROPERTY(identifier, NSNumber) +RCT_EXPORT_VIEW_PROPERTY(onDismiss, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(supportedOrientations, NSArray) RCT_EXPORT_VIEW_PROPERTY(onOrientationChange, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(visible, BOOL) RCT_EXPORT_VIEW_PROPERTY(onRequestClose, RCTDirectEventBlock) -// Fabric only -RCT_EXPORT_VIEW_PROPERTY(onDismiss, RCTDirectEventBlock) - @end diff --git a/packages/react-native/React/Views/RCTModalManager.h b/packages/react-native/React/Views/RCTModalManager.h deleted file mode 100644 index 237037fd8db6b2..00000000000000 --- a/packages/react-native/React/Views/RCTModalManager.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import - -#import -#import - -@interface RCTModalManager : RCTEventEmitter - -- (void)modalDismissed:(NSNumber *)modalID; - -@end diff --git a/packages/react-native/React/Views/RCTModalManager.m b/packages/react-native/React/Views/RCTModalManager.m deleted file mode 100644 index 85ddb29b149036..00000000000000 --- a/packages/react-native/React/Views/RCTModalManager.m +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import "RCTModalManager.h" - -@interface RCTModalManager () - -@property BOOL shouldEmit; - -@end - -@implementation RCTModalManager - -RCT_EXPORT_MODULE(); - -- (NSArray *)supportedEvents -{ - return @[ @"modalDismissed" ]; -} - -- (void)startObserving -{ - _shouldEmit = YES; -} - -- (void)stopObserving -{ - _shouldEmit = NO; -} - -- (void)modalDismissed:(NSNumber *)modalID -{ - if (_shouldEmit) { - [self sendEventWithName:@"modalDismissed" body:@{@"modalID" : modalID}]; - } -} - -@end diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/DismissEvent.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/DismissEvent.java new file mode 100644 index 00000000000000..2b2aeb712647be --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/DismissEvent.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.views.modal; + +import androidx.annotation.Nullable; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.uimanager.common.ViewUtil; +import com.facebook.react.uimanager.events.Event; + +/** {@link Event} for dismissing a Dialog. */ +/* package */ class DismissEvent extends Event { + + public static final String EVENT_NAME = "topDismiss"; + + @Deprecated + protected DismissEvent(int viewTag) { + this(ViewUtil.NO_SURFACE_ID, viewTag); + } + + protected DismissEvent(int surfaceId, int viewTag) { + super(surfaceId, viewTag); + } + + @Override + public String getEventName() { + return EVENT_NAME; + } + + @Nullable + @Override + protected WritableMap getEventData() { + return Arguments.createMap(); + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java index 74c6b8e707c123..241cc550c8a78c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostManager.java @@ -95,7 +95,7 @@ public void setHardwareAccelerated(ReactModalHostView view, boolean hardwareAcce @Override @ReactProp(name = "visible") public void setVisible(ReactModalHostView view, boolean visible) { - // iOS only + view.setVisible(visible); } @Override @@ -110,10 +110,6 @@ public void setAnimated(ReactModalHostView view, boolean value) {} @ReactProp(name = "supportedOrientations") public void setSupportedOrientations(ReactModalHostView view, @Nullable ReadableArray value) {} - @Override - @ReactProp(name = "identifier") - public void setIdentifier(ReactModalHostView view, int value) {} - @Override protected void addEventEmitters( final ThemedReactContext reactContext, final ReactModalHostView view) { @@ -136,6 +132,14 @@ public void onShow(DialogInterface dialog) { new ShowEvent(UIManagerHelper.getSurfaceId(reactContext), view.getId())); } }); + view.setOnDismissListener( + new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(@Nullable DialogInterface dialog) { + dispatcher.dispatchEvent( + new DismissEvent(UIManagerHelper.getSurfaceId(reactContext), view.getId())); + } + }); view.setEventDispatcher(dispatcher); } } @@ -150,7 +154,6 @@ public Map getExportedCustomDirectEventTypeConstants() { MapBuilder.builder() .put(RequestCloseEvent.EVENT_NAME, MapBuilder.of("registrationName", "onRequestClose")) .put(ShowEvent.EVENT_NAME, MapBuilder.of("registrationName", "onShow")) - // iOS only .put("topDismiss", MapBuilder.of("registrationName", "onDismiss")) // iOS only .put("topOrientationChange", MapBuilder.of("registrationName", "onOrientationChange")) @@ -161,7 +164,7 @@ public Map getExportedCustomDirectEventTypeConstants() { @Override protected void onAfterUpdateTransaction(ReactModalHostView view) { super.onAfterUpdateTransaction(view); - view.showOrUpdate(); + view.showOrDismiss(); } @Override diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java index 2ae3bb9bda3c1b..f9fb24b6a03395 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java @@ -81,11 +81,13 @@ public interface OnRequestCloseListener { private boolean mStatusBarTranslucent; private String mAnimationType; private boolean mHardwareAccelerated; + private boolean mVisible; // Set this flag to true if changing a particular property on the view requires a new Dialog to // be created. For instance, animation does since it affects Dialog creation through the theme // but transparency does not since we can access the window to update the property. private boolean mPropertyRequiresNewDialog; private @Nullable DialogInterface.OnShowListener mOnShowListener; + private @Nullable DialogInterface.OnDismissListener mOnDismissListener; private @Nullable OnRequestCloseListener mOnRequestCloseListener; public ReactModalHostView(ThemedReactContext context) { @@ -192,6 +194,10 @@ protected void setOnShowListener(DialogInterface.OnShowListener listener) { mOnShowListener = listener; } + protected void setOnDismissListener(DialogInterface.OnDismissListener listener) { + mOnDismissListener = listener; + } + protected void setTransparent(boolean transparent) { mTransparent = transparent; } @@ -211,6 +217,10 @@ protected void setHardwareAccelerated(boolean hardwareAccelerated) { mPropertyRequiresNewDialog = true; } + protected void setVisible(boolean visible) { + mVisible = visible; + } + void setEventDispatcher(EventDispatcher eventDispatcher) { mHostView.setEventDispatcher(eventDispatcher); } @@ -294,6 +304,7 @@ protected void showOrUpdate() { updateProperties(); mDialog.setOnShowListener(mOnShowListener); + mDialog.setOnDismissListener(mOnDismissListener); mDialog.setOnKeyListener( new DialogInterface.OnKeyListener() { @Override @@ -334,6 +345,14 @@ public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { } } + protected void showOrDismiss() { + if (mVisible) { + showOrUpdate(); + } else { + dismiss(); + } + } + /** * Returns the view that will be the root view of the dialog. We are wrapping this in a * FrameLayout because this is the system's way of notifying us that the dialog size has changed. diff --git a/packages/rn-tester/js/examples/Modal/ModalOnShow.js b/packages/rn-tester/js/examples/Modal/ModalOnShow.js index 8ef9764794a570..ce9bc50c6a9a72 100644 --- a/packages/rn-tester/js/examples/Modal/ModalOnShow.js +++ b/packages/rn-tester/js/examples/Modal/ModalOnShow.js @@ -133,6 +133,6 @@ export default ({ title: "Modal's onShow/onDismiss", name: 'onShow', description: - 'onShow and onDismiss (iOS only) callbacks are called when a modal is shown/dismissed', + 'onShow and onDismiss callbacks are called when a modal is shown/dismissed', render: (): React.Node => , }: RNTesterModuleExample); diff --git a/packages/rn-tester/js/examples/Modal/ModalPresentation.js b/packages/rn-tester/js/examples/Modal/ModalPresentation.js index e5f2cad774ebf1..921cd7251d240a 100644 --- a/packages/rn-tester/js/examples/Modal/ModalPresentation.js +++ b/packages/rn-tester/js/examples/Modal/ModalPresentation.js @@ -199,8 +199,8 @@ function ModalPresentation() { setProps(prev => ({ ...prev,