Skip to content

Commit 8a955b8

Browse files
authored
fix(ios, afterInitialTab): ensure initial tab mounts before other tabs. (#7890)
* fix(ios, afterInitialTab): ensure initial tab mounts before other tabs. Updated `BottomTabsAfterInitialTabAttacher` to asynchronously attach other tabs after the initial tab's `componentDidMount` is called. * test(e2e): add test for mounted screens order (attach after initial tab)
1 parent 6439f68 commit 8a955b8

File tree

6 files changed

+64
-4
lines changed

6 files changed

+64
-4
lines changed

e2e/BottomTabs.test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ describe('BottomTabs', () => {
1111
await expect(elementByLabel('First Tab')).toBeVisible();
1212
});
1313

14+
it.e2e('should mount first tab first', async () => {
15+
await expect(elementById(TestIDs.MOUNTED_SCREENS_TEXT)).toHaveText(
16+
'Mounted screens: FirstBottomTabScreen, SecondBottomTabScreen'
17+
);
18+
});
19+
1420
it('switch to tab by index', async () => {
1521
await elementById(TestIDs.SWITCH_TAB_BY_INDEX_BTN).tap();
1622
await expect(elementByLabel('First Tab')).toBeNotVisible();

lib/ios/BottomTabsAfterInitialTabAttacher.m

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ - (void)attach:(UITabBarController *)bottomTabsController {
77
[bottomTabsController.selectedViewController setReactViewReadyCallback:^{
88
[bottomTabsController readyForPresentation];
99
for (UIViewController *viewController in bottomTabsController.deselectedViewControllers) {
10+
dispatch_async(dispatch_get_main_queue(), ^{
1011
[viewController render];
12+
});
1113
}
1214
}];
1315

playground/ios/NavigationTests/RNNCommandsHandlerTest.m

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -554,8 +554,21 @@ - (void)testSetRoot_withBottomTabsAttachModeAfterInitialTab {
554554
}];
555555

556556
[self waitForExpectationsWithTimeout:10 handler:nil];
557-
XCTAssertTrue(_vc1.isViewLoaded);
558-
XCTAssertTrue(_vc2.isViewLoaded);
557+
558+
XCTAssertTrue(self->_vc1.isViewLoaded);
559+
560+
// Make sure the view is not loaded until the next main run loop.
561+
XCTAssertFalse(self->_vc2.isViewLoaded);
562+
563+
564+
// Wait for the next main run-loop.
565+
XCTestExpectation *viewLoadedExpectation = [self expectationWithDescription:@"Wait for _vc2.isViewLoaded"];
566+
dispatch_async(dispatch_get_main_queue(), ^{
567+
XCTAssertTrue(self->_vc2.isViewLoaded);
568+
[viewLoadedExpectation fulfill];
569+
});
570+
571+
[self waitForExpectationsWithTimeout:1 handler:nil];
559572
}
560573

561574
- (void)testSetRoot_withAnimation {

playground/src/screens/FirstBottomTabScreen.tsx

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1-
import React from 'react';
1+
import React, { Component } from 'react';
22
import { NavigationProps, Options } from 'react-native-navigation';
33
import Root from '../components/Root';
44
import Button from '../components/Button';
55
import Navigation from './../services/Navigation';
66
import Screens from './Screens';
77
import { component } from '../commons/Layouts';
88
import testIDs from '../testIDs';
9+
import { Text } from 'react-native';
10+
11+
export class MountedBottomTabScreensState {
12+
static mountedBottomTabScreens: string[] = [];
13+
static callback: (mountedBottomTabScreens: string[]) => void = () => {};
14+
15+
static addScreen(screen: string) {
16+
this.mountedBottomTabScreens.push(screen);
17+
this.callback(this.mountedBottomTabScreens);
18+
}
19+
}
920

1021
const {
1122
SWITCH_TAB_BY_INDEX_BTN,
@@ -16,9 +27,14 @@ const {
1627
SHOW_TABS_BTN,
1728
HIDE_TABS_PUSH_BTN,
1829
FIRST_TAB_BAR_BUTTON,
30+
MOUNTED_SCREENS_TEXT,
1931
} = testIDs;
2032

21-
export default class FirstBottomTabScreen extends React.Component<NavigationProps> {
33+
interface NavigationState {
34+
mountedBottomTabScreens: string[];
35+
}
36+
37+
export default class FirstBottomTabScreen extends Component<NavigationProps, NavigationState> {
2238
static options(): Options {
2339
return {
2440
layout: {
@@ -38,6 +54,19 @@ export default class FirstBottomTabScreen extends React.Component<NavigationProp
3854
};
3955
}
4056

57+
constructor(props: NavigationProps) {
58+
super(props);
59+
this.state = { mountedBottomTabScreens: [] };
60+
61+
MountedBottomTabScreensState.callback = (mountedBottomTabScreens: string[]) => {
62+
this.setState({ mountedBottomTabScreens: mountedBottomTabScreens });
63+
};
64+
}
65+
66+
componentDidMount() {
67+
MountedBottomTabScreensState.addScreen('FirstBottomTabScreen');
68+
}
69+
4170
badgeVisible = true;
4271
bottomTabPressedListener = Navigation.events().registerBottomTabPressedListener((event) => {
4372
if (event.tabIndex == 2) {
@@ -71,6 +100,10 @@ export default class FirstBottomTabScreen extends React.Component<NavigationProp
71100
/>
72101
<Button label="Push" onPress={this.push} />
73102
<Button label="Add border and shadow" onPress={this.modifyBottomTabs} />
103+
104+
<Text testID={MOUNTED_SCREENS_TEXT}>
105+
Mounted screens: {this.state.mountedBottomTabScreens.join(', ')}
106+
</Text>
74107
</Root>
75108
);
76109
}

playground/src/screens/SecondBottomTabScreen.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Screens from './Screens';
66
import Navigation from './../services/Navigation';
77
import { stack, component } from './../commons/Layouts';
88
import testIDs from '../testIDs';
9+
import { MountedBottomTabScreensState } from './FirstBottomTabScreen';
910

1011
const {
1112
SIDE_MENU_INSIDE_BOTTOM_TABS_BTN,
@@ -40,6 +41,10 @@ export default class SecondBottomTabScreen extends React.Component<NavigationPro
4041
};
4142
}
4243

44+
componentDidMount() {
45+
MountedBottomTabScreensState.addScreen('SecondBottomTabScreen');
46+
}
47+
4348
render() {
4449
return (
4550
<Root componentId={this.props.componentId}>

playground/src/testIDs.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ const testIDs = {
212212
TOGGLE_REACT_DECLARED_MODAL: 'TOGGLE_REACT_DECLARED_MODAL',
213213
REPLACE_TAB_TEST_ID: 'REPLACE_TAB_TEST_ID',
214214
REPLACED_TAB: 'REPLACED_TAB',
215+
MOUNTED_SCREENS_TEXT: 'MOUNTED_SCREENS_TEXT',
215216

216217
//External Component Buttons
217218
EXTERNAL_DISMISS_MODAL_BTN: 'EXTERNAL_DISMISS_MODAL_BTN',

0 commit comments

Comments
 (0)