Skip to content

Commit

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

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

## Changelog

<!-- 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.

Pull Request resolved: #29882

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.

Reviewed By: shergin

Differential Revision: D24648412

Pulled By: hramos

fbshipit-source-id: acf28fef21420117c845d3aed97e47b5dd4e9390
  • Loading branch information
kkoudev authored and facebook-github-bot committed Nov 14, 2020
1 parent 4700075 commit d85d5d2
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 20 deletions.
25 changes: 23 additions & 2 deletions Libraries/Modal/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@

const AppContainer = require('../ReactNative/AppContainer');
const I18nManager = require('../ReactNative/I18nManager');
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 @@ -26,6 +29,11 @@ import type {DirectEventHandler} from '../Types/CodegenTypes';
import {type EventSubscription} from '../vendor/emitter/EventEmitter';
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 @@ -161,9 +169,22 @@ class Modal extends React.Component<Props> {
this._identifier = uniqueModalIdentifier++;
}

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
27 changes: 9 additions & 18 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 @@ -46,8 +47,6 @@ - (void)insertReactSubview:(id<RCTComponent>)subview atIndex:(NSInteger)atIndex

@interface RCTModalHostViewManager () <RCTModalHostViewInteractor>

@property (nonatomic, copy) dispatch_block_t dismissWaitingBlock;

@end

@implementation RCTModalHostViewManager {
Expand Down Expand Up @@ -79,33 +78,25 @@ - (void)presentModalHostView:(RCTModalHostView *)modalHostView
if (_presentationBlock) {
_presentationBlock([modalHostView reactViewController], viewController, animated, completionBlock);
} else {
__weak typeof(self) weakself = self;
[[modalHostView reactViewController] presentViewController:viewController
animated:animated
completion:^{
!completionBlock ?: completionBlock();
__strong typeof(weakself) strongself = weakself;
!strongself.dismissWaitingBlock
?: strongself.dismissWaitingBlock();
strongself.dismissWaitingBlock = nil;
}];
completion:completionBlock];
}
}

- (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 {
self.dismissWaitingBlock = ^{
[viewController.presentingViewController dismissViewControllerAnimated:animated completion:nil];
};
if (viewController.presentingViewController) {
self.dismissWaitingBlock();
self.dismissWaitingBlock = 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
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ void ModalHostViewEventEmitter::onShow(OnShow event) const {
return payload;
});
}
void ModalHostViewEventEmitter::onDismiss(OnDismiss event) const {
dispatchEvent("dismiss", [event=std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);

return payload;
});
}
void ModalHostViewEventEmitter::onOrientationChange(OnOrientationChange event) const {
dispatchEvent("orientationChange", [event=std::move(event)](jsi::Runtime &runtime) {
auto payload = jsi::Object(runtime);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ class ModalHostViewEventEmitter : public ViewEventEmitter {

};

struct OnDismiss {

};

enum class OnOrientationChangeOrientation {
Portrait,
Landscape
Expand All @@ -216,6 +220,8 @@ class ModalHostViewEventEmitter : public ViewEventEmitter {

void onShow(OnShow value) const;

void onDismiss(OnDismiss value) const;

void onOrientationChange(OnOrientationChange value) const;
};

Expand Down

0 comments on commit d85d5d2

Please sign in to comment.