导航对象引用
navigation
对象包含各种便捷功能,用于分发导航操作。它看起来像这样
navigation
navigate
- 转到给定屏幕,这将根据导航器的不同而表现不同goBack
- 返回上一个屏幕,在堆栈中使用时将弹出当前屏幕reset
- 使用给定状态替换导航器的导航状态preload
- 在导航到屏幕之前在后台预加载屏幕setParams
- 将新参数合并到路由的参数上dispatch
- 发送操作对象以更新导航状态setOptions
- 更新屏幕的选项isFocused
- 检查屏幕是否聚焦canGoBack
- 检查是否可以从当前屏幕返回getState
- 获取导航器的导航状态getParent
- 获取父屏幕的导航对象(如果有)addListener
- 订阅屏幕的事件removeListener
- 取消订阅屏幕的事件
navigation
对象可以在任何屏幕组件内部通过useNavigation
hook 访问。它也作为 prop 传递给使用动态 API 定义的屏幕组件。
setParams
/setOptions
等只应在事件监听器或 useEffect
/useLayoutEffect
/componentDidMount
/componentDidUpdate
等中调用。不要在渲染或构造函数中调用。
依赖于导航器的功能
navigation
对象上存在一些额外的功能,具体取决于当前导航器的类型。
如果导航器是堆栈导航器,则提供了 navigate
和 goBack
的几种替代方法,您可以选择您喜欢的方法。这些功能是
navigation
replace
- 用新屏幕替换当前屏幕push
- 将新屏幕推入堆栈pop
- 在堆栈中返回popTo
- 返回堆栈中的特定屏幕popToTop
- 返回堆栈的顶部
有关这些方法的更多详细信息,请参阅堆栈导航器助手和原生堆栈导航器助手。
如果导航器是标签导航器,则以下功能也可用
navigation
jumpTo
- 跳转到标签导航器中的特定屏幕
有关这些方法的更多详细信息,请参阅底部标签导航器助手和Material Top Tab 导航器助手。
如果导航器是抽屉导航器,则以下功能也可用
navigation
jumpTo
- 跳转到抽屉导航器中的特定屏幕openDrawer
- 打开抽屉closeDrawer
- 关闭抽屉toggleDrawer
- 切换状态,即从关闭切换到打开,反之亦然
有关这些方法的更多详细信息,请参阅抽屉导航器助手。
通用 API 参考
您与 navigation
对象的大部分交互将涉及 navigate
、goBack
和 setParams
。
navigate
navigate
方法允许我们导航到应用程序中的另一个屏幕。它接受以下参数
navigation.navigate(name, params)
name
- string - 当前或父导航器中屏幕的目标名称。params
- object - 用于目标路由的参数。merge
- boolean - 参数是否应与现有路由参数合并,或者替换它们(当导航到现有屏幕时)。默认为false
。
- 静态
- 动态
function HomeScreen() {
const navigation = useNavigation();
return (
<View
style={{
flex: 1,
gap: 8,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text>This is the home screen of the app</Text>
<Button
onPress={() => {
navigation.navigate('Profile', {
names: ['Brent', 'Satya', 'Michaś'],
});
}}
>
Go to Brent's profile
</Button>
</View>
);
}
function HomeScreen() {
const navigation = useNavigation();
return (
<View
style={{
flex: 1,
gap: 8,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text>This is the home screen of the app</Text>
<Button
onPress={() => {
navigation.navigate('Profile', {
names: ['Brent', 'Satya', 'Michaś'],
});
}}
>
Go to Brent's profile
</Button>
</View>
);
}
在堆栈导航器(堆栈或原生堆栈)中,使用屏幕名称调用 navigate
将具有以下行为
- 如果您已在具有相同名称的屏幕上,它将更新其参数,而不会推送新屏幕。
- 如果您在不同的屏幕上,它将把新屏幕推入堆栈。
- 如果指定了
getId
prop,并且堆栈中的另一个屏幕具有相同的 ID,它将使该屏幕成为焦点并更新其参数。 - 如果以上条件均不匹配,它将把新屏幕推入堆栈。
默认情况下,屏幕由其名称标识。但是您也可以通过使用getId
prop 自定义它以考虑参数。
例如,假设您为 Profile
屏幕指定了 getId
prop
- 静态
- 动态
const Tabs = createBottomTabNavigator({
screens: {
Profile: {
screen: ProfileScreen,
getId: ({ params }) => params.userId,
},
},
});
<Tab.Screen
name={Profile}
component={ProfileScreen}
getId={({ params }) => params.userId}
/>
现在,如果您有一个堆栈,其历史记录为 Home > Profile (userId: bob) > Settings
,并且您调用 navigate(Profile, { userId: 'alice' })
,则生成的屏幕将为 Home > Profile (userId: bob) > Settings > Profile (userId: alice)
,因为它将添加一个新的 Profile
屏幕,因为没有找到匹配的屏幕。
在标签或抽屉导航器中,调用 navigate
将切换到相关的屏幕(如果尚未聚焦)并更新屏幕的参数。
navigateDeprecated
此方法已弃用,将在未来的版本中删除。它的存在仅出于兼容性目的。请改用 navigate
。
navigateDeprecated
操作实现了先前版本中 navigate
的旧行为。
它接受以下参数
navigation.navigateDeprecated(name, params)
name
- string - 当前或父导航器中屏幕的目标名称。params
- object - 用于目标路由的参数。
在堆栈导航器(堆栈或原生堆栈)中,使用屏幕名称调用 navigate
将具有以下行为
- 如果您已在具有相同名称的屏幕上,它将更新其参数,而不会推送新屏幕。
- 如果堆栈中已存在具有相同名称的屏幕,它将弹出其后的所有屏幕以返回到现有屏幕。
- 如果指定了
getId
prop,并且堆栈中的另一个屏幕具有相同的 ID,它将弹出任何屏幕以导航到该屏幕并更新其参数。 - 如果以上条件均不匹配,它将把新屏幕推入堆栈。
在标签或抽屉导航器中,调用 navigate
将切换到相关的屏幕(如果尚未聚焦)并更新屏幕的参数。
goBack
goBack
方法允许我们返回到导航器中的上一个屏幕。
默认情况下,goBack
将从调用它的屏幕返回
- 静态
- 动态
function ProfileScreen({ route }) {
const navigation = useNavigation();
return (
<View
style={{
flex: 1,
gap: 8,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text>Profile Screen</Text>
<Text>Friends: </Text>
<Text>{route.params.names[0]}</Text>
<Text>{route.params.names[1]}</Text>
<Text>{route.params.names[2]}</Text>
<Button onPress={() => navigation.goBack()}>Go back</Button>
</View>
);
}
function ProfileScreen({ route }) {
const navigation = useNavigation();
return (
<View
style={{
flex: 1,
gap: 8,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text>Profile Screen</Text>
<Text>Friends: </Text>
<Text>{route.params.names[0]}</Text>
<Text>{route.params.names[1]}</Text>
<Text>{route.params.names[2]}</Text>
<Button onPress={() => navigation.goBack()}>Go back</Button>
</View>
);
}
reset
reset
方法允许我们用新状态替换导航器状态
- 静态
- 动态
navigation.reset({
index: 0,
routes: [
{
name: 'Settings',
params: { someParam: 'Param1' },
},
],
});
navigation.reset({
index: 0,
routes: [
{
name: 'Settings',
params: { someParam: 'Param1' },
},
],
});
在 reset
中指定的状态对象将现有导航状态替换为新状态,即删除现有屏幕并添加新屏幕。如果您希望在更改状态时保留现有屏幕,则可以使用CommonActions.reset
和 dispatch
。
请将导航器的状态对象视为内部对象,并且可能会在次要版本中更改。除非您真的需要,否则请避免使用导航状态状态对象中的属性,但 index
和 routes
除外。如果有些功能您无法在不依赖状态对象结构的情况下实现,请打开一个 issue。
preload
preload
方法允许在导航到屏幕之前在后台预加载屏幕。它接受以下参数
name
- string - 当前或父导航器中屏幕的目标名称。params
- object - 用于目标路由的参数。
- 静态
- 动态
function HomeScreen() {
const navigation = useNavigation();
return (
<View
style={{
flex: 1,
gap: 8,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text>Home!</Text>
<Button
onPress={() => {
navigation.preload('Profile', { user: 'jane' });
}}
>
Preload Profile
</Button>
<Button
onPress={() => {
navigation.navigate('Profile', { user: 'jane' });
}}
>
Navigate to Profile
</Button>
</View>
);
}
function HomeScreen() {
const navigation = useNavigation();
return (
<View
style={{
flex: 1,
gap: 8,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text>Home!</Text>
<Button
onPress={() => {
navigation.preload('Profile', { user: 'jane' });
}}
>
Preload Profile
</Button>
<Button
onPress={() => {
navigation.navigate('Profile', { user: 'jane' });
}}
>
Navigate to Profile
</Button>
</View>
);
}
预加载屏幕意味着屏幕将在后台呈现。屏幕中的所有组件都将被挂载,并且将调用 useEffect
hook。当您想通过隐藏挂载重组件或加载数据时的延迟来提高感知性能时,这可能很有用。
根据导航器的不同,preload
的工作方式可能略有不同
- 在堆栈导航器(堆栈,原生堆栈)中,屏幕将在屏幕外呈现,并在您导航到屏幕时以动画形式显示。如果指定了
getId
,它将用于导航以识别预加载的屏幕。 - 在标签或抽屉导航器(底部标签,material top tabs,抽屉等)中,现有屏幕将呈现为好像
lazy
设置为false
。对已呈现的屏幕调用preload
将不会有任何效果。
当屏幕在堆栈导航器中预加载时,它将有一些限制
- 它无法分发导航操作(例如
navigate
、goBack
等)。 - 它无法使用
navigation.setOptions
更新选项。 - 它无法监听来自导航器的事件(例如
focus
、tabPress
等)。
一旦您导航到屏幕,navigation
对象将被更新。因此,如果您在 useEffect
hook 中有一个事件侦听器,并且依赖于 navigation
,它将在导航到屏幕时添加任何侦听器
React.useEffect(() => {
const unsubscribe = navigation.addListener('tabPress', () => {
// do something
});
return () => {
unsubscribe();
};
}, [navigation]);
同样,对于分发操作或更新选项,您可以先检查屏幕是否聚焦
if (navigation.isFocused()) {
navigation.setOptions({ title: 'Updated title' });
}
setParams
setParams
方法允许我们更新当前屏幕的参数 (route.params
)。setParams
的工作方式类似于 React 的 setState
- 它将提供的参数对象与当前参数浅合并。
- 静态
- 动态
function ProfileScreen({ route }) {
const navigation = useNavigation();
return (
<View
style={{
flex: 1,
gap: 8,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text>Profile Screen</Text>
<Text>Friends: </Text>
<Text>{route.params.friends[0]}</Text>
<Text>{route.params.friends[1]}</Text>
<Text>{route.params.friends[2]}</Text>
<Button
onPress={() => {
navigation.setParams({
friends:
route.params.friends[0] === 'Brent'
? ['Wojciech', 'Szymon', 'Jakub']
: ['Brent', 'Satya', 'Michaś'],
title:
route.params.title === "Brent's Profile"
? "Lucy's Profile"
: "Brent's Profile",
});
}}
>
Swap title and friends
</Button>
<Button onPress={() => navigation.goBack()}>Go back</Button>
</View>
);
}
function ProfileScreen({ route }) {
const navigation = useNavigation();
return (
<View
style={{
flex: 1,
gap: 8,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text>Profile Screen</Text>
<Text>Friends: </Text>
<Text>{route.params.friends[0]}</Text>
<Text>{route.params.friends[1]}</Text>
<Text>{route.params.friends[2]}</Text>
<Button
onPress={() => {
navigation.setParams({
friends:
route.params.friends[0] === 'Brent'
? ['Wojciech', 'Szymon', 'Jakub']
: ['Brent', 'Satya', 'Michaś'],
title:
route.params.title === "Brent's Profile"
? "Lucy's Profile"
: "Brent's Profile",
});
}}
>
Swap title and friends
</Button>
<Button onPress={() => navigation.goBack()}>Go back</Button>
</View>
);
}
setOptions
setOptions
方法允许我们从组件内部设置屏幕选项。如果我们需要使用组件的 props、state 或 context 来配置屏幕,这将非常有用。
- 静态
- 动态
function ProfileScreen({ route }) {
const navigation = useNavigation();
const [value, onChangeText] = React.useState(route.params.title);
React.useEffect(() => {
navigation.setOptions({
title: value === '' ? 'No title' : value,
});
}, [navigation, value]);
return (
<View
style={{
flex: 1,
gap: 8,
alignItems: 'center',
justifyContent: 'center',
}}
>
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={onChangeText}
value={value}
/>
<Button onPress={() => navigation.goBack()}>Go back</Button>
</View>
);
}
function ProfileScreen({ route }) {
const navigation = useNavigation();
const [value, onChangeText] = React.useState(route.params.title);
React.useEffect(() => {
navigation.setOptions({
title: value === '' ? 'No title' : value,
});
}, [navigation, value]);
return (
<View
style={{
flex: 1,
gap: 8,
alignItems: 'center',
justifyContent: 'center',
}}
>
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={onChangeText}
value={value}
/>
<Button onPress={() => navigation.goBack()}>Go back</Button>
</View>
);
}
此处指定的任何选项都将与定义屏幕时指定的选项浅合并。
当使用 navigation.setOptions
时,我们建议在屏幕的 options
prop 中指定一个占位符,并使用 navigation.setOptions
更新它。这确保了更新选项的延迟不会被用户注意到。它也使其适用于延迟加载的屏幕。
您还可以使用 React.useLayoutEffect
来减少更新选项的延迟。但是如果您支持 web 并进行服务器端渲染,我们建议不要这样做。
navigation.setOptions
旨在提供在必要时更新现有选项的能力。它不能替代屏幕上的 options
prop。请确保仅在绝对必要时才谨慎使用 navigation.setOptions
。
导航事件
屏幕可以使用 addListener
方法在 navigation
对象上添加侦听器。例如,要监听 focus
事件
- 静态
- 动态
function ProfileScreen() {
const navigation = useNavigation();
React.useEffect(
() => navigation.addListener('focus', () => alert('Screen was focused')),
[navigation]
);
React.useEffect(
() => navigation.addListener('blur', () => alert('Screen was unfocused')),
[navigation]
);
return (
<View
style={{
flex: 1,
gap: 8,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text>Profile Screen</Text>
<Button onPress={() => navigation.navigate('Settings')}>
Go to Settings
</Button>
</View>
);
}
function ProfileScreen() {
const navigation = useNavigation();
React.useEffect(
() => navigation.addListener('focus', () => alert('Screen was focused')),
[navigation]
);
React.useEffect(
() => navigation.addListener('blur', () => alert('Screen was unfocused')),
[navigation]
);
return (
<View
style={{
flex: 1,
gap: 8,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text>Profile Screen</Text>
<Button onPress={() => navigation.navigate('Settings')}>
Go to Settings
</Button>
</View>
);
}
有关可用事件和 API 用法的更多详细信息,请参阅导航事件。
isFocused
此方法允许我们检查屏幕当前是否聚焦。如果屏幕聚焦,则返回 true
,否则返回 false
。
const isFocused = navigation.isFocused();
此方法在值更改时不会重新渲染屏幕,主要在回调中很有用。您可能需要使用useIsFocused 而不是直接使用此方法,它将返回一个布尔值 prop 以指示屏幕是否聚焦。
高级 API 参考
dispatch
函数不太常用,但如果您无法使用可用的方法(如 navigate
、goBack
等)完成所需操作,则它是一个很好的应急方案。我们建议避免经常使用 dispatch
方法,除非绝对必要。
dispatch
dispatch
方法允许我们发送一个导航操作对象,该对象确定导航状态将如何更新。所有导航功能(如 navigate
)都在幕后使用 dispatch
。
请注意,如果您想分发操作,则应使用此库中提供的操作创建器,而不是直接编写操作对象。
有关可用操作的完整列表,请参阅导航操作文档。
import { CommonActions } from '@react-navigation/native';
navigation.dispatch(
CommonActions.navigate({
name: 'Profile',
params: {},
})
);
分发操作对象时,您还可以指定一些其他属性
source
- 应被视为操作来源的路由的键。例如,replace
操作将替换具有给定键的路由。默认情况下,它将使用分发操作的路由的键。您可以显式传递undefined
以覆盖此行为。target
- 应在其上应用操作的导航状态的键。默认情况下,如果操作未被导航器处理,则操作会冒泡到其他导航器。如果指定了target
,则如果具有相同键的导航器未处理该操作,则该操作不会冒泡。
示例
import { CommonActions } from '@react-navigation/native';
navigation.dispatch({
...CommonActions.navigate('Profile'),
source: 'someRoutekey',
target: 'someStatekey',
});
自定义操作创建器
也可以将操作创建器函数传递给 dispatch
。该函数将接收当前状态,并且需要返回要使用的导航操作对象
import { CommonActions } from '@react-navigation/native';
navigation.dispatch((state) => {
// Add the home route to the start of the stack
const routes = [{ name: 'Home' }, ...state.routes];
return CommonActions.reset({
...state,
routes,
index: routes.length - 1,
});
});
您可以使用此功能构建您自己的助手,以便在您的应用程序中使用。这是一个示例,它实现了在倒数第二个屏幕之前插入屏幕
import { CommonActions } from '@react-navigation/native';
const insertBeforeLast = (routeName, params) => (state) => {
const routes = [
...state.routes.slice(0, -1),
{ name: routeName, params },
state.routes[state.routes.length - 1],
];
return CommonActions.reset({
...state,
routes,
index: routes.length - 1,
});
};
然后像这样使用它
navigation.dispatch(insertBeforeLast('Home'));
canGoBack
此方法返回一个布尔值,指示当前导航器或任何父导航器中是否有任何导航历史记录可用。您可以使用它来检查是否可以调用 navigation.goBack()
if (navigation.canGoBack()) {
navigation.goBack();
}
不要使用此方法渲染内容,因为这不会触发重新渲染。此方法仅适用于回调、事件侦听器等内部。
getParent
此方法从当前导航器嵌套在其中的父导航器返回导航对象。例如,如果您有一个堆栈导航器和一个嵌套在堆栈内部的标签导航器,则可以在标签导航器的屏幕内部使用 getParent
来获取从堆栈导航器传递的导航对象。
它接受一个可选的 ID 参数来引用特定的父导航器。例如,如果您的屏幕嵌套在抽屉导航器下的多个嵌套级别中,并且 id
prop 为 "LeftDrawer"
,则您可以直接引用它,而无需多次调用 getParent
。
要为导航器使用 ID,请首先传递唯一的 id
prop
- 静态
- 动态
const Drawer = createDrawerNavigator({
id: 'LeftDrawer',
screens: {
/* content */
},
});
<Drawer.Navigator id="LeftDrawer">{/* .. */}</Drawer.Navigator>
然后在使用 getParent
时,而不是
// Avoid this
const drawerNavigation = navigation.getParent().getParent();
// ...
drawerNavigation?.openDrawer();
您可以这样做
// Do this
const drawerNavigation = navigation.getParent('LeftDrawer');
// ...
drawerNavigation?.openDrawer();
这种方法允许组件不必知道导航器的嵌套结构。因此,强烈建议在使用 getParent
时使用 id
。
如果没有匹配的父导航器,此方法将返回 undefined
。使用此方法时,请务必始终检查 undefined
。
getState
请将导航器的状态对象视为内部对象,并且可能会在次要版本中更改。除非您真的需要,否则请避免使用导航状态状态对象中的属性,但 index
和 routes
除外。如果有些功能您无法在不依赖状态对象结构的情况下实现,请打开一个 issue。
此方法返回包含屏幕的导航器的状态对象。在极少数情况下,获取导航器状态可能很有用。您很可能不需要使用此方法。如果使用,请确保您有充分的理由。
如果您需要状态来渲染内容,则应使用useNavigationState
而不是此方法。