Skip to content

Commit

Permalink
Fix cannot working Modal's onDismiss
Browse files Browse the repository at this point in the history
Summary:
Fixes: facebook#29455

Modal's onDismiss is not called on iOS.
This issue occurred the commit facebook@bd2b7d6 and was fixed the commit facebook@27a3248.
However, the master and stable-0.63 branches do not have this modified commit applied to them.

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->

[iOS] [Fixed] - Modal's onDismiss prop will now be called successfully.

Test Plan:
Tested on iOS with this change:

1. Set function Modal's onDismiss prop.
1. Set Modal's visible props is true. (show Modal)
1. Set Modal's visible props is false. (close Modal)
1. The set function in onDismiss is called.
  • Loading branch information
kkoudev authored and sandfox committed Jan 5, 2021
1 parent 2765172 commit 2692b92
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 4 deletions.
26 changes: 24 additions & 2 deletions Libraries/Modal/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
const AppContainer = require('../ReactNative/AppContainer');
const I18nManager = require('../ReactNative/I18nManager');
const PropTypes = require('prop-types');
import NativeEventEmitter from '../EventEmitter/NativeEventEmitter';
import NativeModalManager from './NativeModalManager';
const Platform = require('../Utilities/Platform');
const React = require('react');
const ScrollView = require('../Components/ScrollView/ScrollView');
const StyleSheet = require('../StyleSheet/StyleSheet');
Expand All @@ -22,6 +25,12 @@ import type {ViewProps} from '../Components/View/ViewPropTypes';
import type {DirectEventHandler} from '../Types/CodegenTypes';
import type EmitterSubscription from '../vendor/emitter/EmitterSubscription';
import RCTModalHostView from './RCTModalHostViewNativeComponent';

const ModalEventEmitter =
Platform.OS === 'ios' && NativeModalManager != null
? new NativeEventEmitter(NativeModalManager)
: null;

/**
* The Modal component is a simple way to present content above an enclosing view.
*
Expand Down Expand Up @@ -173,9 +182,22 @@ class Modal extends React.Component<Props> {
};
}

componentDidMount() {
if (ModalEventEmitter) {
this._eventSubscription = ModalEventEmitter.addListener(
'modalDismissed',
event => {
if (event.modalID === this._identifier && this.props.onDismiss) {
this.props.onDismiss();
}
},
);
}
}

componentWillUnmount() {
if (this.props.onDismiss != null) {
this.props.onDismiss();
if (this._eventSubscription) {
this._eventSubscription.remove();
}
}

Expand Down
9 changes: 9 additions & 0 deletions Libraries/Modal/RCTModalHostViewNativeComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {HostComponent} from '../Renderer/shims/ReactNativeTypes';
import type {
WithDefault,
DirectEventHandler,
BubblingEventHandler,
Int32,
} from '../Types/CodegenTypes';

Expand Down Expand Up @@ -86,6 +87,14 @@ type NativeProps = $ReadOnly<{|
*/
onShow?: ?DirectEventHandler<null>,

/**
* The `onDismiss` prop allows passing a function that will be called once
* the modal has been dismissed.
*
* See https://reactnative.dev/docs/modal.html#ondismiss
*/
onDismiss?: ?BubblingEventHandler<null>,

/**
* Deprecated. Use the `animationType` prop instead.
*/
Expand Down
10 changes: 8 additions & 2 deletions React/Views/RCTModalHostViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#import "RCTBridge.h"
#import "RCTModalHostView.h"
#import "RCTModalHostViewController.h"
#import "RCTModalManager.h"
#import "RCTShadowView.h"
#import "RCTUtils.h"

Expand Down Expand Up @@ -89,10 +90,15 @@ - (void)dismissModalHostView:(RCTModalHostView *)modalHostView
withViewController:(RCTModalHostViewController *)viewController
animated:(BOOL)animated
{
dispatch_block_t completionBlock = ^{
if (modalHostView.identifier) {
[[self.bridge moduleForClass:[RCTModalManager class]] modalDismissed:modalHostView.identifier];
}
};
if (_dismissalBlock) {
_dismissalBlock([modalHostView reactViewController], viewController, animated, nil);
_dismissalBlock([modalHostView reactViewController], viewController, animated, completionBlock);
} else {
[viewController.presentingViewController dismissViewControllerAnimated:animated completion:nil];
[viewController.presentingViewController dismissViewControllerAnimated:animated completion:completionBlock];
}
}

Expand Down
17 changes: 17 additions & 0 deletions React/Views/RCTModalManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#import <UIKit/UIKit.h>

#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

@interface RCTModalManager : RCTEventEmitter <RCTBridgeModule>

- (void)modalDismissed:(NSNumber *)modalID;

@end
42 changes: 42 additions & 0 deletions React/Views/RCTModalManager.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) Facebook, Inc. and its 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<NSString *> *)supportedEvents
{
return @[ @"modalDismissed" ];
}

- (void)startObserving
{
_shouldEmit = YES;
}

- (void)stopObserving
{
_shouldEmit = NO;
}

- (void)modalDismissed:(NSNumber *)modalID
{
if (_shouldEmit) {
[self sendEventWithName:@"modalDismissed" body:@{@"modalID" : modalID}];
}
}

@end

0 comments on commit 2692b92

Please sign in to comment.