这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战
options中的属性:
-
跨页面的公共设置
<Stack.Navigator screenOptions={{ headerStyle: { backgroundColor: '#f4511e', }, headerTintColor: '#fff', headerTitleStyle: { fontWeight: 'bold', }, }} > <Stack.Screen name="Home" component={HomeScreen} options={{ title: '首页' }} /> </Stack.Navigator>
-
title:标题
<Stack.Navigator> <Stack.Screen name="Home" component={HomeScreen} options={{ title: '首页' }} /> </Stack.Navigator>
-
使用参数传递过来的标题
传入选项函数的参数是具有以下属性的对象:
navigation
- 页面的导航route
- 页面的路由;<Stack.Navigator> <Stack.Screen name="Home" component={ProfileScreen} options={({ route }) => ({ title: route.params.name })} /> </Stack.Navigator>
-
setOptions() 设置tabbar组件上的属性
navigation.setOptions({ title: 'Updated!' })
-
头部标题栏
-
headerStyle
设置头部标题栏样式headerStyle: { backgroundColor: 'red', },
-
headerTinColor
:设置头部标题栏字体颜色 -
headerTitleStyle
:设置头部标题样式headerTitleStyle: { fontWeight: 'bold', fontSize: 30, },
-
-
自定义头部标题栏
headerTitle
自定义标题文字,可以传入一个组件headerBackTitleVisible
是否显示返回按钮的文字headerBackTitle
设置返回按钮的文字headerRight
设置头部标题右边样式,可传入一个组件headerBackImage
自定义返回图标,返回一个图片组件
嵌套导航器意味着在另一个导航器的屏幕内呈现导航器;一般主页面都会有tabbar栏,如下:
import React, { Component } from 'react';
import { NavigationContainer } from '@react-navigation/native';
import {
createStackNavigator,
HeaderBackButton,
StackNavigationProp,
} from '@react-navigation/stack';
import Found from '../pages/found/Found';
import List from '../pages/list/List';
import Me from '../pages/me/Me';
import BottomTabs from './ButtomTab';
import Detail from '../pages/detail/Detail';
import Login from '../pages/login/Login';
import Search from '../pages/search/Search';
import Play from '../pages/play/Play';
import MVPlay from '../pages/mv-play/MVPlay';
import CustomTitleBar from '../pages/custom-title-bar/CustomTitleBar';
import CustomHeader from '../components/CustomHeader';
import { Button } from '@ant-design/react-native';
import { Alert, Image } from 'react-native';
export type RootStackParamList = {
BottomTabs: undefined;
List: undefined;
Found: undefined;
Me: undefined;
Login: undefined;
Detail: {
topId: number;
};
Search: undefined;
Play: {
id: number;
};
MVPlay: {
vid: string;
};
CustomTitleBar: undefined;
};
export type RootStackNavigation = StackNavigationProp<RootStackParamList>;
const Stack = createStackNavigator<RootStackParamList>();
export default class Navigator extends Component {
render() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="BottomTabs"
component={BottomTabs}
options={{
headerTitle: '首页',
headerStyle: {
backgroundColor: 'red',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
fontSize: 30,
},
}}
/>
<Stack.Screen
name="List"
component={List}
options={{ headerTitle: '榜单' }}
/>
<Stack.Screen
name="Found"
component={Found}
options={{ headerTitle: '发现' }}
/>
<Stack.Screen
name="Me"
component={Me}
options={{ headerTitle: '我的' }}
/>
<Stack.Screen
name="Login"
component={Login}
options={{ headerTitle: '登录' }}
/>
<Stack.Screen
name="Detail"
component={Detail}
options={{ headerTitle: '歌单' }}
/>
<Stack.Screen
name="Search"
component={Search}
options={{ headerTitle: '搜索' }}
/>
<Stack.Screen
name="Play"
component={Play}
options={{ headerTitle: '歌曲' }}
/>
<Stack.Screen
name="MVPlay"
component={MVPlay}
options={{ headerTitle: 'mv' }}
/>
<Stack.Screen
name="CustomTitleBar"
component={CustomTitleBar}
options={{
headerTitle: (props) => <CustomHeader {...props} />,
// headerBackTitleVisible: false,
headerBackTitle: '1234',
headerRight: () => (
<Button onPress={() => Alert.alert('right')}>
right
</Button>
),
headerBackImage: () => (
<Image
source={{
uri: 'https://qpic.y.qq.com/music_cover/DhpicvGxCZozibtVUC0Q03Oia0h9DnKUNHPdPL3oD2tqUJiaYJUv1jvlEXbPvCCy4Vql/300?n=1',
}}
style={[
{
width: 60,
height: 60,
marginRight: 20,
},
]}
/>
),
}}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
}
完整代码如下:
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import React, { Component } from 'react';
import {
getFocusedRouteNameFromRoute,
RouteProp,
TabNavigationState,
} from '@react-navigation/native';
import { RootStackNavigation, RootStackParamList } from './index';
import Home from '../pages/home/Home';
import List from '../pages/list/List';
import Found from '../pages/found/Found';
import Me from '../pages/me/Me';
export type BottomTabParamList = {
Home: undefined;
List: undefined;
Found: undefined;
Me: undefined;
};
const Tab = createBottomTabNavigator<BottomTabParamList>();
type Route = RouteProp<RootStackParamList, 'BottomTabs'> & {
state?: TabNavigationState<BottomTabParamList>;
};
interface IProps {
navigation: RootStackNavigation;
route: Route;
}
export default class BottomTabs extends Component<IProps> {
// 获取每个页面的标题
getHeaderTitle(route: Route): string {
const routeName = getFocusedRouteNameFromRoute(route) ?? 'Home';
switch (routeName) {
case 'Home':
return '首页';
case 'List':
return '榜单';
case 'Found':
return '发现';
case 'Me':
return '我的';
default:
return '首页';
}
}
componentDidUpdate() {
const { navigation, route } = this.props;
navigation.setOptions({
headerTitle: this.getHeaderTitle(route),
});
}
render() {
return (
<Tab.Navigator
tabBarOptions={{
activeTintColor: '#f86442', // 修改tabbar激活颜色
}}>
<Tab.Screen
name="Home"
component={Home}
options={{ tabBarLabel: '首页' }}
/>
<Tab.Screen
name="List"
component={List}
options={{ tabBarLabel: '榜单' }}
/>
<Tab.Screen
name="Found"
component={Found}
options={{ tabBarLabel: '发现' }}
/>
<Tab.Screen
name="Me"
component={Me}
options={{ tabBarLabel: '我的' }}
/>
</Tab.Navigator>
);
}
}
其中首页、榜单、发现、我的,这几个页面都有个底部tabbar;这就是一个嵌套路由,首次加载的就是这个有tabbar的组件,点击tabbar的按钮可以切换。
React Navigation的生命周期
我们使用了一个堆栈导航器,它有两个屏幕(Home和Detail),并学习了如何使用navigation.navigate('RouteName')
在路由之间导航。
在这种情况下,一个重要的问题是:当我们离开它时,或者当我们回到它时,Home会发生什么? 路由如何发现用户是离开它还是返回它?
如果你要从一个web背景来进行反应式导航,你可以假设当用户从路由a导航到路由B时,a将卸载(它的componentWillUnmount
被调用),而a将在用户返回到它时再次加载。虽然这些React生命周期方法仍然有效,并且在React -navigation中使用,但它们的用法不同于web。这是由更复杂的移动导航需求驱动的。
比如一个带有屏幕a和b的堆栈导航器。导航到a后,调用它的componentDidMount
。当push
B时,它的componentDidMount
也会被调用,但是A仍然挂载在堆栈上,因此它的componentWillUnmount
不会被调用。
当从B返回到A时,B的componentWillUnmount
被调用,但A的componentDidMount
没有被调用,因为A一直保持挂载;类似的结果也可以结合嵌套导航。考虑一个有两个标签的标签导航器,其中每个标签都是堆栈导航器:
import * as React from 'react';
import { Button, View, Text } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
function SettingsScreen({ navigation }) {
return (
<View
style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Settings Screen</Text>
<Button
title="Go to Profile"
onPress={() => navigation.navigate('Profile')}
/>
</View>
);
}
function ProfileScreen({ navigation }) {
return (
<View
style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Profile Screen</Text>
<Button
title="Go to Settings"
onPress={() => navigation.navigate('Settings')}
/>
</View>
);
}
function HomeScreen({ navigation }) {
return (
<View
style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
</View>
);
}
function DetailsScreen({ navigation }) {
return (
<View
style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
<Button
title="Go to Details... again"
onPress={() => navigation.push('Details')}
/>
</View>
);
}
const Tab = createBottomTabNavigator();
const SettingsStack = createStackNavigator();
const HomeStack = createStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="First">
{() => (
<SettingsStack.Navigator>
<SettingsStack.Screen
name="Settings"
component={SettingsScreen}
/>
<SettingsStack.Screen
name="Profile"
component={ProfileScreen}
/>
</SettingsStack.Navigator>
)}
</Tab.Screen>
<Tab.Screen name="Second">
{() => (
<HomeStack.Navigator>
<HomeStack.Screen
name="Home"
component={HomeScreen}
/>
<HomeStack.Screen
name="Details"
component={DetailsScreen}
/>
</HomeStack.Navigator>
)}
</Tab.Screen>
</Tab.Navigator>
</NavigationContainer>
);
}
我们从
HomeScreen
到DetailScreen
,然后使用tab栏切换到SetterScreen
并导航到profileScreen
,在这一系列操作完成后,挂载了4个屏幕组件,如果你使用标签栏切换回HomeStack,你会注意到你会看到DetailsScreen - HomeStack的导航状态被保留了!
react navigation声明周期中的事件
foucs
监听对焦事件;和html
的input
标签的focus
事件干的事一样
blur
监听超出焦点事件;和html
的input
标签的blur
事件干的事一样
function Profile({ navigation }) {
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// Screen was focused
// Do something
});
return unsubscribe;
}, [navigation]);
return <ProfileContent />;
}
在函数组件中可以使用
useFoucsEffect
这个钩子函数来监听执行事件
import { useFocusEffect } from '@react-navigation/native';
function Profile() {
useFocusEffect(
React.useCallback(() => {
// Do something when the screen is focused
return () => {
// Do something when the screen is unfocused
// Useful for cleanup functions
};
}, [])
);
return <ProfileContent />;
}