REACT NAVIGATION August 29, 2018

【React Navigation】十一、导航器嵌套使用的一些注意事项

Words count 27k Reading time 25 mins. Read count 0

每个页面都可以配置来决定它在导航器中的呈现方式。在基础文档的配置标题栏部分,我们介绍了它的工作原理。

在本文档中,我们将会解释当有多个导航器一起使用时它是如何工作的。看完该章节,你将学习到如何将navigationOptions放在正确的位置,并确保可以正确的配置导航器。如果你把它们放在错误的地方,有可能什么都不会发生,但也可能会发生令人困惑和意外的事情。值得庆幸的是,以下的环节理解起来相对比较简单。

只能从导航器的某个屏幕组件来修改它的导航选项。这同样适用于导航器嵌套使用的情况

试想这样的一种情形:一个选项卡导航器,导航器的每个tab又包含了一个堆栈导航器。假如我们在堆栈导航器的某个屏幕配置navigationOptions,会产生什么效果?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class A extends React.Component {
static navigationOptions = {
tabBarLabel: 'Home!',
};

// etc..
}

class B extends React.Component {
static navigationOptions = {
tabBarLabel: 'Settings!',
};

// etc..
}

let HomeStack = createStackNavigator({ A });
let SettingsStack = createStackNavigator({ B });

export default createBottomTabNavigator({
HomeStack,
SettingsStack,
});

正如我们上边提到的:只能从导航器的某个屏幕组件来修改它的导航选项。上边代码中的A和B是分别属于HomeStack和SettingStack的屏幕组件,而不是属于选项卡导航器的屏幕组件。因此,当我们在A和B配置tabBarLabel属性时,在选项卡导航器上是没有任何效果的。我们将代码稍作修改,来达到我们想要的效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import React from 'react';
import { View, Text } from 'react-native';
import {
createStackNavigator,
createBottomTabNavigator,
} from 'react-navigation';

const Placeholder = ({ text }) => (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>{text}</Text>
</View>
);

class A extends React.Component {
render() {
return <Placeholder text="A!" />;
}
}

class B extends React.Component {
render() {
return <Placeholder text="B!" />;
}
}

let HomeStack = createStackNavigator({ A });
let SettingsStack = createStackNavigator({ B });

HomeStack.navigationOptions = {
tabBarLabel: 'Home!',
};

SettingsStack.navigationOptions = {
tabBarLabel: 'Settings!',
};

export default createBottomTabNavigator({
HomeStack,
SettingsStack,
});

为了弄清上边的逻辑,我们先回想以下的代码:MyComponent和MyOtherComponent两个组件的效果是一样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyComponent extends React.Component {
static navigationOptions = {
title: 'Hello!',
};
// etc.
}

class MyOtherComponent extends React.Component {
// etc.
}

MyOtherComponent.navigationOptions = {
title: 'Hello!',
};

另外,我们也了解到,createStackNavigator函数和相关的一些函数是可以返回一个react组件的。因此,当HomeStack和SettingStack作为某个导航器的屏幕组件时,我们直接在HomeStack和SettingStack配置navigationOptions时,相当于我们控制了它父类导航器的navigationOptions。

堆栈导航器嵌套一个选项卡导航器,并希望设置堆栈导航器的title

1
2
3
4
5
6
7
8
9
const TabNavigator = createBottomTabNavigator({
Feed: FeedScreen,
Profile: ProfileScreen,
});

const AppNavigator = createStackNavigator({
Home: TabNavigator,
Settings: SettingsScreen,
});

以上边的代码为例,假如我们在FeedScreen页面中,通过navigationOptions配置headerTitle属性,将不会达到我们想要的效果。因为AppNavigator堆栈导航器只会查找它的直接子屏幕(TabNavigator和SettingScreen)的配置。因此,为了设置AppNavigator的title,我们可以通过TabNavigator来它的headerTitle。如下方的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const TabNavigator = createBottomTabNavigator({
Feed: FeedScreen,
Profile: ProfileScreen,
});

TabNavigator.navigationOptions = ({ navigation }) => {
let { routeName } = navigation.state.routes[navigation.state.index];

// You can do whatever you like here to pick the title based on the route name
let headerTitle = routeName;

return {
headerTitle,
};
};

还有另外一种情形:每个tab页面有自己的堆栈导航器,你可以选择当某个tab被选中时,隐藏顶层堆栈导航器的标题部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const FeedStack = createStackNavigator({
FeedHome: FeedScreen,
/* other routes here */
});

const ProfileStack = createStackNavigator({
ProfileHome: ProfileScreen,
/* other routes here */
});

const TabNavigator = createBottomTabNavigator({
Feed: FeedStack,
Profile: ProfileStack,
});

TabNavigator.navigationOptions = {
// Hide the header from AppNavigator stack
header: null,
};

const AppNavigator = createStackNavigator({
Home: TabNavigator,
Settings: SettingsScreen,
});

选项卡导航器包含一个堆栈导航器,你希望在特殊的页面隐藏选项卡工具条

1
2
3
4
5
6
7
8
9
const FeedStack = createStackNavigator({
FeedHome: FeedScreen,
Details: DetailsScreen,
});

const TabNavigator = createBottomTabNavigator({
Feed: FeedStack,
Profile: ProfileScreen,
});

以上边的代码为例,假如希望当从feed页面跳转到detials页面时,将选项卡工具条隐藏。我们不能通过在DetailsScreen的navigationOptions中设置tabBarVisible:false这种写法来达到效果,因为这些配置只会作用于FeedStack。为了达到这个效果,我们需要按照以下这种写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import React from 'react';
import { View, Text, TouchableHighlight } from 'react-native';
import {
createStackNavigator,
createBottomTabNavigator,
} from 'react-navigation';

const Placeholder = ({ text }) => (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>{text}</Text>
</View>
);

class FeedScreen extends React.Component {


render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<TouchableHighlight onPress={() => this.props.navigation.navigate('Details')}>
<Text>go to Details</Text>
</TouchableHighlight>

</View>
);
}
}
FeedScreen.navigationOptions = {
title: "FeedScreen",
};

class DetailsScreen extends React.Component {
render() {
return <Placeholder text="DetailsScreen!" />;
}
}
DetailsScreen.navigationOptions = {
title: "DetailsScreen",
};

class ProfileScreen extends React.Component {
render() {
return <Placeholder text="ProfileScreen!" />;
}
}

const FeedStack = createStackNavigator({
FeedHome: FeedScreen,
Details: DetailsScreen,
});

export default createBottomTabNavigator({
Feed: FeedStack,
Profile: ProfileScreen,
});

FeedStack.navigationOptions = ({ navigation }) => {
let tabBarVisible = true;
if (navigation.state.index > 0) {
tabBarVisible = false;
}

return {
tabBarVisible,
};
};

抽屉里面有一个堆栈导航器,你希望在某些特定屏幕上锁定抽屉功能

这与上一小节比较类似,上一小节是在特定的屏幕隐藏选项卡工具条,而这一小节是在特定的屏幕锁定抽屉功能,使用的是drawerLockMode属性而不是tabBarVisible属性

1
2
3
4
5
6
7
8
9
const FeedStack = createStackNavigator({
FeedHome: FeedScreen,
Details: DetailsScreen,
});

const DrawerNavigator = createDrawerNavigator({
Feed: FeedStack,
Profile: ProfileScreen,
});

为了实当我们跳转到Details页面时将抽屉隐藏的效果。我们需要在FeedStack中配置navigationOptions,因为假如我们在DetailsScreen中配置navigationOptions ,它将直接作用于它的父类导航器FeedStack,而不是抽屉导航器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import React from 'react';
import { View, Text, TouchableHighlight } from 'react-native';
import {
createStackNavigator,
createBottomTabNavigator,
createDrawerNavigator,
} from 'react-navigation';

const Placeholder = ({ text }) => (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>{text}</Text>
</View>
);

class FeedScreen extends React.Component {


render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<TouchableHighlight onPress={() => this.props.navigation.navigate('Details')}>
<Text>go to Details</Text>
</TouchableHighlight>

</View>
);
}
}
FeedScreen.navigationOptions = {
title: "FeedScreen",
};

class DetailsScreen extends React.Component {
render() {
return <Placeholder text="DetailsScreen!" />;
}
}
DetailsScreen.navigationOptions = {
title: "DetailsScreen",
};

class ProfileScreen extends React.Component {
render() {
return <Placeholder text="ProfileScreen!" />;
}
}

const FeedStack = createStackNavigator({
FeedHome: FeedScreen,
Details: DetailsScreen,
});

export default createDrawerNavigator({
Feed: FeedStack,
Profile: ProfileScreen,
});

FeedStack.navigationOptions = ({ navigation }) => {
let drawerLockMode = 'unlocked';
if (navigation.state.index > 0) {
drawerLockMode = 'locked-closed';
}

return {
drawerLockMode,
};
};
0%