跳到主要内容
版本: 7.x

导航对象引用

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 对象上存在一些额外的功能,具体取决于当前导航器的类型。

如果导航器是堆栈导航器,则提供了 navigategoBack 的几种替代方法,您可以选择您喜欢的方法。这些功能是

  • navigation
    • replace - 用新屏幕替换当前屏幕
    • push - 将新屏幕推入堆栈
    • pop - 在堆栈中返回
    • popTo - 返回堆栈中的特定屏幕
    • popToTop - 返回堆栈的顶部

有关这些方法的更多详细信息,请参阅堆栈导航器助手原生堆栈导航器助手

如果导航器是标签导航器,则以下功能也可用

  • navigation
    • jumpTo - 跳转到标签导航器中的特定屏幕

有关这些方法的更多详细信息,请参阅底部标签导航器助手Material Top Tab 导航器助手

如果导航器是抽屉导航器,则以下功能也可用

  • navigation
    • jumpTo - 跳转到抽屉导航器中的特定屏幕
    • openDrawer - 打开抽屉
    • closeDrawer - 关闭抽屉
    • toggleDrawer - 切换状态,即从关闭切换到打开,反之亦然

有关这些方法的更多详细信息,请参阅抽屉导航器助手

通用 API 参考

您与 navigation 对象的大部分交互将涉及 navigategoBacksetParams

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>
);
}
Snack 上尝试

在堆栈导航器(堆栈原生堆栈)中,使用屏幕名称调用 navigate 将具有以下行为

  • 如果您已在具有相同名称的屏幕上,它将更新其参数,而不会推送新屏幕。
  • 如果您在不同的屏幕上,它将把新屏幕推入堆栈。
  • 如果指定了getId prop,并且堆栈中的另一个屏幕具有相同的 ID,它将使该屏幕成为焦点并更新其参数。
  • 如果以上条件均不匹配,它将把新屏幕推入堆栈。

默认情况下,屏幕由其名称标识。但是您也可以通过使用getId prop 自定义它以考虑参数。

例如,假设您为 Profile 屏幕指定了 getId prop

const Tabs = createBottomTabNavigator({
screens: {
Profile: {
screen: ProfileScreen,
getId: ({ params }) => params.userId,
},
},
});

现在,如果您有一个堆栈,其历史记录为 Home > Profile (userId: bob) > Settings,并且您调用 navigate(Profile, { userId: 'alice' }),则生成的屏幕将为 Home > Profile (userId: bob) > Settings > Profile (userId: alice),因为它将添加一个新的 Profile 屏幕,因为没有找到匹配的屏幕。

在标签或抽屉导航器中,调用 navigate 将切换到相关的屏幕(如果尚未聚焦)并更新屏幕的参数。

警告

此方法已弃用,将在未来的版本中删除。它的存在仅出于兼容性目的。请改用 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>
);
}
Snack 上尝试

reset

reset 方法允许我们用新状态替换导航器状态

navigation.reset({
index: 0,
routes: [
{
name: 'Settings',
params: { someParam: 'Param1' },
},
],
});
Snack 上尝试

reset 中指定的状态对象将现有导航状态替换为新状态,即删除现有屏幕并添加新屏幕。如果您希望在更改状态时保留现有屏幕,则可以使用CommonActions.resetdispatch

警告

请将导航器的状态对象视为内部对象,并且可能会在次要版本中更改。除非您真的需要,否则请避免使用导航状态状态对象中的属性,但 indexroutes 除外。如果有些功能您无法在不依赖状态对象结构的情况下实现,请打开一个 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>
);
}
Snack 上尝试

预加载屏幕意味着屏幕将在后台呈现。屏幕中的所有组件都将被挂载,并且将调用 useEffect hook。当您想通过隐藏挂载重组件或加载数据时的延迟来提高感知性能时,这可能很有用。

根据导航器的不同,preload 的工作方式可能略有不同

  • 在堆栈导航器(堆栈原生堆栈)中,屏幕将在屏幕外呈现,并在您导航到屏幕时以动画形式显示。如果指定了getId,它将用于导航以识别预加载的屏幕。
  • 在标签或抽屉导航器(底部标签material top tabs抽屉等)中,现有屏幕将呈现为好像 lazy 设置为 false。对已呈现的屏幕调用 preload 将不会有任何效果。

当屏幕在堆栈导航器中预加载时,它将有一些限制

  • 它无法分发导航操作(例如 navigategoBack 等)。
  • 它无法使用 navigation.setOptions 更新选项。
  • 它无法监听来自导航器的事件(例如 focustabPress 等)。

一旦您导航到屏幕,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>
);
}
Snack 上尝试

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>
);
}
Snack 上尝试

此处指定的任何选项都将与定义屏幕时指定的选项浅合并。

当使用 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>
);
}
Snack 上尝试

有关可用事件和 API 用法的更多详细信息,请参阅导航事件

isFocused

此方法允许我们检查屏幕当前是否聚焦。如果屏幕聚焦,则返回 true,否则返回 false

const isFocused = navigation.isFocused();

此方法在值更改时不会重新渲染屏幕,主要在回调中很有用。您可能需要使用useIsFocused 而不是直接使用此方法,它将返回一个布尔值 prop 以指示屏幕是否聚焦。

高级 API 参考

dispatch 函数不太常用,但如果您无法使用可用的方法(如 navigategoBack 等)完成所需操作,则它是一个很好的应急方案。我们建议避免经常使用 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 */
},
});

然后在使用 getParent 时,而不是

// Avoid this
const drawerNavigation = navigation.getParent().getParent();

// ...

drawerNavigation?.openDrawer();

您可以这样做

// Do this
const drawerNavigation = navigation.getParent('LeftDrawer');

// ...

drawerNavigation?.openDrawer();

这种方法允许组件不必知道导航器的嵌套结构。因此,强烈建议在使用 getParent 时使用 id

如果没有匹配的父导航器,此方法将返回 undefined。使用此方法时,请务必始终检查 undefined

getState

警告

请将导航器的状态对象视为内部对象,并且可能会在次要版本中更改。除非您真的需要,否则请避免使用导航状态状态对象中的属性,但 indexroutes 除外。如果有些功能您无法在不依赖状态对象结构的情况下实现,请打开一个 issue。

此方法返回包含屏幕的导航器的状态对象。在极少数情况下,获取导航器状态可能很有用。您很可能不需要使用此方法。如果使用,请确保您有充分的理由。

如果您需要状态来渲染内容,则应使用useNavigationState 而不是此方法。