diff --git a/e2e/StaticLifecycleEvents.test.js b/e2e/StaticLifecycleEvents.test.js index 042993195df..3697f4a4ad4 100644 --- a/e2e/StaticLifecycleEvents.test.js +++ b/e2e/StaticLifecycleEvents.test.js @@ -60,4 +60,11 @@ describe('static lifecycle events', () => { await elementById(TestIDs.PUSH_BTN).tap(); await expect(elementByLabel('componentDidDisappear | ReactTitleView | TopBarTitle')).toBeVisible(); }); + + it('unmounts previous root before resolving setRoot promise', async () => { + await elementById(TestIDs.SET_ROOT_BTN).tap(); + await elementById(TestIDs.SET_ROOT_BTN).tap(); + + await expect(elementByLabel('setRoot complete - previous root is unmounted')).toBeVisible(); + }); }); diff --git a/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/Navigator.java b/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/Navigator.java index 079032e90d2..de0e7a71da2 100644 --- a/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/Navigator.java +++ b/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/Navigator.java @@ -144,8 +144,8 @@ public void setRoot(final ViewController viewController, CommandListener command @Override public void onSuccess(String childId) { if (removeSplashView) contentLayout.removeViewAt(0); - super.onSuccess(childId); destroyPreviousRoot(); + super.onSuccess(childId); } }, reactInstanceManager); } diff --git a/lib/ios/RNNCommandsHandler.m b/lib/ios/RNNCommandsHandler.m index ae8be5257f3..96fa67ef1f5 100644 --- a/lib/ios/RNNCommandsHandler.m +++ b/lib/ios/RNNCommandsHandler.m @@ -71,17 +71,18 @@ - (void)setRoot:(NSDictionary*)layout commandId:(NSString*)commandId completion: } [_modalManager dismissAllModalsAnimated:NO completion:nil]; - - UIViewController *vc = [_controllerFactory createLayout:layout[@"root"]]; + + UIViewController *vc = [_controllerFactory createLayout:layout[@"root"]]; vc.waitForRender = [vc.resolveOptionsWithDefault.animations.setRoot.waitForRender getWithDefaultValue:NO]; __weak UIViewController* weakVC = vc; [vc setReactViewReadyCallback:^{ + [self->_mainWindow.rootViewController destroy]; self->_mainWindow.rootViewController = weakVC; [self->_eventEmitter sendOnNavigationCommandCompletion:setRoot commandId:commandId params:@{@"layout": layout}]; completion(); }]; - [vc render]; + [vc render]; } - (void)mergeOptions:(NSString*)componentId options:(NSDictionary*)mergeOptions completion:(RNNTransitionCompletionBlock)completion { diff --git a/lib/ios/RNNComponentViewController.h b/lib/ios/RNNComponentViewController.h index 544dfd6c4e3..65c5b69b593 100644 --- a/lib/ios/RNNComponentViewController.h +++ b/lib/ios/RNNComponentViewController.h @@ -29,4 +29,6 @@ typedef void (^PreviewCallback)(UIViewController *vc); - (void)onButtonPress:(RNNUIBarButtonItem *)barButtonItem; +- (void)destroyReactView; + @end diff --git a/lib/ios/RNNComponentViewController.m b/lib/ios/RNNComponentViewController.m index 6550a40bc61..236b9ca51c5 100644 --- a/lib/ios/RNNComponentViewController.m +++ b/lib/ios/RNNComponentViewController.m @@ -49,6 +49,12 @@ - (void)render { [self renderReactViewIfNeeded]; } +- (void)destroyReactView { + if ([self.view isKindOfClass: [RNNReactView class]]) { + [((RNNReactView *)self.view) invalidate]; + } +} + - (void)renderReactViewIfNeeded { if (!self.isViewLoaded) { self.view = [self.creator createRootView:self.layoutInfo.name diff --git a/lib/ios/RNNReactView.h b/lib/ios/RNNReactView.h index 7d15a06a3c1..87109173bd9 100644 --- a/lib/ios/RNNReactView.h +++ b/lib/ios/RNNReactView.h @@ -26,4 +26,6 @@ typedef void (^RNNReactViewReadyCompletionBlock)(void); - (void)componentDidDisappear; +- (void)invalidate; + @end diff --git a/lib/ios/RNNReactView.m b/lib/ios/RNNReactView.m index 38fa3d4e8e4..0c20592111e 100644 --- a/lib/ios/RNNReactView.m +++ b/lib/ios/RNNReactView.m @@ -1,4 +1,5 @@ #import "RNNReactView.h" +#import @implementation RNNReactView { BOOL _isAppeared; @@ -45,6 +46,10 @@ - (void)componentDidDisappear { _isAppeared = NO; } +- (void)invalidate { + [((RCTRootContentView *)self.contentView) invalidate]; +} + - (NSString *)componentId { return self.appProperties[@"componentId"]; } diff --git a/lib/ios/UIViewController+LayoutProtocol.h b/lib/ios/UIViewController+LayoutProtocol.h index d5103c7d273..87e3ca7efc9 100644 --- a/lib/ios/UIViewController+LayoutProtocol.h +++ b/lib/ios/UIViewController+LayoutProtocol.h @@ -16,6 +16,8 @@ typedef void (^RNNReactViewReadyCompletionBlock)(void); - (UIViewController *)topMostViewController; +- (void)destroy; + - (void)mergeOptions:(RNNNavigationOptions *)options; - (void)mergeChildOptions:(RNNNavigationOptions *)options child:(UIViewController *)child; diff --git a/lib/ios/UIViewController+LayoutProtocol.m b/lib/ios/UIViewController+LayoutProtocol.m index 2044d00a479..ccd8c53e296 100644 --- a/lib/ios/UIViewController+LayoutProtocol.m +++ b/lib/ios/UIViewController+LayoutProtocol.m @@ -93,6 +93,19 @@ - (UIViewController *)getCurrentChild { return nil; } +- (void)destroy { + [self destroyReactView]; + [self.presentedViewController destroy]; + + for (UIViewController* child in self.childViewControllers) { + [child destroy]; + } +} + +- (void)destroyReactView { + +} + - (UIViewController *)presentedComponentViewController { UIViewController* currentChild = self.getCurrentChild; return currentChild ? currentChild.presentedComponentViewController : self; diff --git a/playground/src/screens/SetRootScreen.js b/playground/src/screens/SetRootScreen.js index cdbadb804ae..908ebab6125 100644 --- a/playground/src/screens/SetRootScreen.js +++ b/playground/src/screens/SetRootScreen.js @@ -4,9 +4,12 @@ const Button = require('../components/Button') const Navigation = require('./../services/Navigation'); const { NAVIGATION_TAB, - SET_MULTIPLE_ROOTS_BTN + SET_MULTIPLE_ROOTS_BTN, + SET_ROOT_BTN } = require('../testIDs'); const Screens = require('./Screens'); +const { logLifecycleEvent } = require('./StaticLifecycleOverlay'); +let unmounted; class SetRootScreen extends React.Component { static options() { @@ -24,14 +27,29 @@ class SetRootScreen extends React.Component { }; } + constructor(props) { + super(props); + unmounted = false; + } + render() { return ( +