封面 pid: 77554327
版本5.x,安装和更多使用参考官网
试水(以StackNavigator为例)
基本使用
createStackNavigator()
返回一个包含Screen
和Navigator
的对象,用于配置导航,Screen
(用来配置路由)需要包含在Navigator
中。NavigationContainer
组件用于组织所有导航结构,通常该组件用在app的最顶层。
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
function Home({ 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 Details() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
</View>
);
}
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={Home} options={{ title: 'Overview' }} />
<Stack.Screen name="Details" component={Details} options={{ title: 'Details' }} />
{/*往Screen传props来配置路由*/}
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
被路由绑定的页面组件可以通过props.navigation
取得navigation
对象,通过navigation.navigate(name,params?)
方法进行跳转,通过navigation.goBack()
方法返回。
注:如果已经在目标路由,多次navigate()
到该路由是无效的,如果需要忽略该限制可以使用navigation.push(name,params?)
方法
navigation.navigate(name,params?)
中的params对象用于跳转传参,在目标组件中通过props.route.params
取得参数,通过navigation.setParams()
来修改参数。Screen
组件可以接收initialParams
props来指定路由初始化参数。
options
Screen
组件接收options
props来进行一些额外的配置,如头栏的配置。options
接收一个对象,如果options
需要依赖params/navigation,也可以接收一个返回对象的函数。
return (
<Stack.Navigator>
<Stack.Screen
name="Home"
component={Home}
options={{ title: 'My home' }}
{/*options={({ route,navigation }) => ({ title: route.params.name })}*/}
/>
</Stack.Navigator>
);
如果需要修改当前路由的options
,可以使用navigation.setOptions()
方法
<Button
title="Update the title"
onPress={() => navigation.setOptions({ title: 'Updated!' })}
/>
一些修改头栏样式的options
headerStyle
: 头栏样式headerTintColor
: 头栏标题和返回按钮的色调headerTitleStyle
: 头栏标题样式(字体相关)headerShown
:是否显示头栏
如果多个路由需要共享options
,可以把这部分options
上提到Navigator
组件的screenOptions
props。
<Stack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: '#f4511e',
},
headerTintColor: '#fff',
}}
>
<Stack.Screen
name="Home"
component={Home}
options={{ title: 'My home' }}
/>
</Stack.Navigator>
如果你想使用自定义组件代替头栏标题,可以使用headerTitle
这个options,接收形如props => <MyCom {...props} />
的函数。如果想在头栏两侧加自定义按钮,可以使用header(Left|Right)
这个options,接收一个返回component的函数
<Stack.Navigator>
<Stack.Screen
name="Home"
component={Home}
options={{
headerTitle: props => <MyCom {...props} />,
headerRight: () => (
<Button
onPress={() => alert('This is a button!')}
title="Info"
color="#fff"
/>
),
}}
/>
</Stack.Navigator>
注:options
中的this
指向并不是被绑定的组件,如果需要在这些按钮中与组件互动,按钮应该在组件内通过navigation.setOptions()
进行设置
function Home({ navigation }) {
const [count, setCount] = React.useState(0);
React.useLayoutEffect(() => {
navigation.setOptions({
headerRight: () => (
<Button onPress={() => setCount(c => c + 1)} title="Update count" />
),
});
}, [navigation]);
return <Text>Count: {count}</Text>;
}
默认情况下HeaderLeft
预留给了默认的返回按钮,显示为一个左箭头和上一个路由的标题(当标题过长时以‘back’代替),当然你可以重写HeaderLeft
。
嵌套导航
一个导航组件作为另一个导航的Screen
,这就是嵌套导航,如下
function Home() {
return (
<Tab.Navigator>
<Tab.Screen name="Feed" component={Feed} />
<Tab.Screen name="Messages" component={Messages} />
</Tab.Navigator>
);
}
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Profile" component={Profile} />
<Stack.Screen name="Settings" component={Settings} />
</Stack.Navigator>
</NavigationContainer>
);
}
Home组件包含了一个tab导航,但同时在App组件中它也作为stack导航的Screen,形成如下导航结构
App
(Stack.Navigator)Home
(Tab.Navigator)Feed
(Screen)Messages
(Screen)
Profile
(Screen)Settings
(Screen)
注意以下的点:
- 精良减少嵌套层数,避免错误嵌套
- 每个导航维护着自己的历史
- 每个导航维护着自己的
options
,不会相互干扰 - 每个Screen维护着自己的
params
- 如果当前导航无法处理跳转操作,传递给父导航处理(如跨级跳转)
- 导航特定的方法如
openDrawer, closeDrawer, toggleDrawer
可以穿透 - 子导航默认不接收父导航事件,如果需要请使用
navigation.dangerouslyGetParent().addEventListener()
- 默认情况下父导航的UI渲染在子导航的UI上(请考虑嵌套顺序,常用的有drawer包含stack,stack包含tab,tab包含stack)
有时嵌套的stack导航需要让子导航的头栏渲染而父导航的头栏不渲染,这时可以在父导航的options
设置headerShown:false
来改变UI渲染规则。有时父子的头栏都要渲染,你可以给子导航的options
设置headerShown:true
跳转可以是Screen
到Navigator
(跨层)或Screen
到Screen
(同层)。以上面代码为例,如果你在Profile编程式导航到Home(跨层),则默认跳转到Feed(Navigator
的第一个Screen
)。如果需要在跨层时指定要跳转的Screen
,navigate()
方法接收第二个参数指定Screen
,第二参数也可以同时指定跳转传参
navigation.navigate('Home', {
screen: 'Messages',
params: { user: 'jane' }
});
如果导航嵌套得很深,跳转时可以使用嵌套的Screen
navigation.navigate('Root', {
screen: 'Settings',
params: {
screen: 'Sound',
params: {
screen: 'Media',
},
},
});
//跳到Root->Settings->Sound->Media
生命周期
stack 导航的声明周期遵循堆栈的特性,进入A(A入栈),由A跳到B(B入栈),B返回A(B出栈,还剩A在栈内)。一个Screen
如果在栈内,就会维持mounted状态,只有在出栈时才会unMount。
既然未出栈的Screen
不会被销毁,那它就可以监听离开/返回该Screen
的事件。这可以通过useFocusEffect
这个Hook实现
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 />;
}
DrawerNavigator
yarn add @react-navigation/drawer
抽屉导航栏的开关切换有特别的方法,而且能穿透给子导航调用
navigation.openDrawer()
navigation.closeDrawer()
navigation.toggleDrawer()
提供一个useIsDrawerOpen
Hook来获取一个变量判断抽屉是否开启。
import { useIsDrawerOpen } from '@react-navigation/drawer';
// ...
const isDrawerOpen = useIsDrawerOpen();
props
props
作用于Navigator
组件
sceneContainerStyle
- 包裹Screen
组件容器的样式drawerStyle
-抽屉样式drawerContent
-自定义抽屉内容,接收props=>component
drawerContentOptions
-接收objectactiveTintColor
:选中时导航项标题、图标的颜色activeBackgroundColor
:选中时导航项背景色inactiveTintColor/inactiveBackgroundColor
:非选中时itemStyle
:导航项容器样式labelStyle
:导航项图标容器样式contentContainerStyle
:DrawerContentScrollView
样式style
:DrawerContentScrollView
容器样式
options
options
作用于Screen
组件
title
-兜底导航项标题drawerLabel
-导航项标题,接收接收string或({focused, color}) => component
drawerIcon
-导航项图标,接收({focused, color, size}) => component
- 更多options
自定义DrawerContent
觉得默认抽屉导航丑可以自定义抽屉内容,加些头像什么的。这要用到drawerContent
options。完全重写默认抽屉要用到DrawerContentScrollView
、DrawerItem
组件。自定义的抽屉组件会收到一个{state,navigation,descriptors,progress}
的props
const DrawerContent = (props) => {
const { state: { routes }, navigation } = props;
return (
<>
{/*这里写一些额外的组件如头像什么的*/}
<DrawerContentScrollView {...props}>
{
routes.map(route => {
return (
<DrawerItem key={route.key}
label={({ focused, color }) => <Text>{route.name}</Text>}
onPress={() => navigation.navigate(route.name)}
/>
);
})
}
</DrawerContentScrollView>
</>
);
};
const RootDrawer = () => {
return (
<NavigationContainer>
<Drawer.Navigator
initialRouteName="首页"
drawerContent={DrawerContent}
>
<Drawer.Screen name="首页" component={Home} />
<Drawer.Screen name="设置" component={Setting} />
</Drawer.Navigator>
</NavigationContainer>
);
};
DrawerContentScrollView
用于包裹导航项,DrawerItem
组件用于重写导航项组件,DrawerItem
接收以下props
label
-自定义导航项标题,接收string、({ focused, color }) => component
icon
-自定义导航项图标,接收({ focused, color, size }) => component
focused
-指示当前导航项是否选中onPress
-点击回调,通常要跳转activeTintColor/activeBackgroundColor
inactiveTintColor/inactiveBackgroundColor
labelStyle
style
- 导航项样式
注:这些props和Navigator
组件drawerContentOptions
props中有重合部分,但是一旦你选择重写导航项,后者的配置是不生效的
TabNavigator
yarn add @react-navigation/bottom-tabs
基本使用和StackNavigator相似,但是一些options
和props
不一样
props
props
作用于Navigator
组件
tabBar
-自定义tabBar,接收props=>component
tabBarOptions
-接收objectactiveTintColor/activeBackgroundColor
inactiveTintColor/inactiveBackgroundColor
tabStyle
:导航项样式showLabel
:是否显示导航项标题,接收boollabelStyle
:导航项标题样式labelPosition
:导航项标题位置,接收enum(below-icon,beside-icon)style
:tabBar样式
options
options
作用于Screen
组件
tabBarVisible
-是否显示tabBar,接收booltitle
-兜底导航项标题,没设置headerTitle
或\*Label
时启用tabBarLabel
-导航项标题,接收string或({focused, color}) => component
tabBarIcon
-导航项图标,接收({focused, color, size}) => component
tabBarButton
-导航项按钮,它包含了标题和图标,需要实现onPress实现跳转,接收props =>component
tabBarBadge
-tab图标右上角气泡显示,接收num/string- 更多options
Comments | ?? 条评论