Skip to content

Commit

Permalink
Unmount previous root before resolving setRoot promise (#6197)
Browse files Browse the repository at this point in the history
* Unmount previous root before resolving setRoot promise

* Unmount hirerchy on setRoot

* Fix tests

Co-authored-by: yogevbd <[email protected]>
  • Loading branch information
guyca and yogevbd authored May 17, 2020
1 parent 3e9a2f2 commit 86b344c
Show file tree
Hide file tree
Showing 13 changed files with 89 additions and 8 deletions.
7 changes: 7 additions & 0 deletions e2e/StaticLifecycleEvents.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
7 changes: 4 additions & 3 deletions lib/ios/RNNCommandsHandler.m
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions lib/ios/RNNComponentViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,6 @@ typedef void (^PreviewCallback)(UIViewController *vc);

- (void)onButtonPress:(RNNUIBarButtonItem *)barButtonItem;

- (void)destroyReactView;

@end
6 changes: 6 additions & 0 deletions lib/ios/RNNComponentViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions lib/ios/RNNReactView.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@ typedef void (^RNNReactViewReadyCompletionBlock)(void);

- (void)componentDidDisappear;

- (void)invalidate;

@end
5 changes: 5 additions & 0 deletions lib/ios/RNNReactView.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#import "RNNReactView.h"
#import <React/RCTRootContentView.h>

@implementation RNNReactView {
BOOL _isAppeared;
Expand Down Expand Up @@ -45,6 +46,10 @@ - (void)componentDidDisappear {
_isAppeared = NO;
}

- (void)invalidate {
[((RCTRootContentView *)self.contentView) invalidate];
}

- (NSString *)componentId {
return self.appProperties[@"componentId"];
}
Expand Down
2 changes: 2 additions & 0 deletions lib/ios/UIViewController+LayoutProtocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ typedef void (^RNNReactViewReadyCompletionBlock)(void);

- (UIViewController *)topMostViewController;

- (void)destroy;

- (void)mergeOptions:(RNNNavigationOptions *)options;

- (void)mergeChildOptions:(RNNNavigationOptions *)options child:(UIViewController *)child;
Expand Down
13 changes: 13 additions & 0 deletions lib/ios/UIViewController+LayoutProtocol.m
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
20 changes: 19 additions & 1 deletion playground/src/screens/SetRootScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -24,14 +27,29 @@ class SetRootScreen extends React.Component {
};
}

constructor(props) {
super(props);
unmounted = false;
}

render() {
return (
<Root componentId={this.props.componentId}>
<Button label='Set Root' testID={SET_ROOT_BTN} onPress={this.setSingleRoot} />
<Button label='Set Multiple Roots' testID={SET_MULTIPLE_ROOTS_BTN} onPress={this.setMultipleRoot} />
</Root>
);
}

componentWillUnmount() {
unmounted = true;
}

setSingleRoot = async () => {
await this.setRoot();
logLifecycleEvent({text: `setRoot complete - previous root is${unmounted ? '' : ' not'} unmounted`});
}

setMultipleRoot = async () => {
await this.setRoot();
await this.setRoot();
Expand Down
6 changes: 5 additions & 1 deletion playground/src/screens/StaticEventsScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ const {
PUSH_BTN,
POP_BTN,
STATIC_EVENTS_OVERLAY_BTN,
MODAL_BTN
MODAL_BTN,
SET_ROOT_BTN

} = require('../testIDs');
const Screens = require('./Screens');

Expand All @@ -18,6 +20,7 @@ class StaticEventsScreen extends React.Component {
<Button label='Push' testID={PUSH_BTN} onPress={this.push} />
<Button label='Pop' testID={POP_BTN} onPress={this.pop} />
<Button label='Show Modal' testID={MODAL_BTN} onPress={this.showModal} />
<Button label='Set Root' testID={SET_ROOT_BTN} onPress={this.setRoot} />
</Root>
);
}
Expand All @@ -37,6 +40,7 @@ class StaticEventsScreen extends React.Component {
});
push = () => Navigation.push(this, Screens.Pushed);
pop = () => Navigation.pop(this);
setRoot = () => Navigation.setRoot(Screens.SetRoot);
}

module.exports = StaticEventsScreen;
23 changes: 22 additions & 1 deletion playground/src/screens/StaticLifecycleOverlay.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ const { View, Text, TouchableOpacity } = require('react-native');
const { Navigation } = require('react-native-navigation');
const TestIDs = require('../testIDs');

let _overlayInstance;
const logLifecycleEvent = (event) => {
_overlayInstance.setState({
events: [..._overlayInstance.state.events, event]
});
}

class StaticLifecycleOverlay extends Component {
static options() {
return {
Expand All @@ -12,6 +19,15 @@ class StaticLifecycleOverlay extends Component {
}
}
}

componentDidMount() {
_overlayInstance = this;
}

componentWillUnmount() {
_overlayInstance = null;
}

constructor(props) {
super(props);
this.state = {
Expand Down Expand Up @@ -63,6 +79,8 @@ class StaticLifecycleOverlay extends Component {
return <Text style={styles.h2}>{`${event.event} | ${event.componentName} | ${event.componentType}`}</Text>;
} else if (event.buttonId) {
return <Text style={styles.h2}>{`${event.event} | ${event.buttonId}`}</Text>;
} else if (event.text){
return <Text style={styles.h2}>{`${event.text}`}</Text>;
} else {
return <Text style={styles.h2}>{`${event.event} | ${event.componentId}`}</Text>;
}
Expand Down Expand Up @@ -109,7 +127,10 @@ class StaticLifecycleOverlay extends Component {
);
}
}
module.exports = StaticLifecycleOverlay;
module.exports = {
StaticLifecycleOverlay,
logLifecycleEvent
}

const styles = {
root: {
Expand Down
2 changes: 1 addition & 1 deletion playground/src/screens/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function registerScreens() {
Navigation.registerComponent(Screens.CocktailDetailsScreen, () => require('./sharedElementTransition/CocktailDetailsScreen'));
Navigation.registerComponent(Screens.CocktailsListScreen, () => require('./sharedElementTransition/CocktailsListScreen'));
Navigation.registerComponent(Screens.CocktailsListMasterScreen, () => require('./splitView/CocktailsListMasterScreen'));
Navigation.registerComponent(Screens.EventsOverlay, () => require('./StaticLifecycleOverlay'));
Navigation.registerComponent(Screens.EventsOverlay, () => require('./StaticLifecycleOverlay').StaticLifecycleOverlay);
Navigation.registerComponent(Screens.EventsScreen, () => require('./StaticEventsScreen'));
Navigation.registerComponent(Screens.ExternalComponent, () => require('./ExternalComponentScreen'));
Navigation.registerComponent(Screens.FirstBottomTabsScreen, () => require('./FirstBottomTabScreen'));
Expand Down

0 comments on commit 86b344c

Please sign in to comment.