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

屏幕之间移动

在前一节中,我们定义了一个带有两个路由(HomeDetails)的堆栈导航器,但我们没有学习如何让用户从 Home 导航到 Details(虽然我们确实学习了如何在代码中更改初始路由,但为了查看另一个屏幕而强迫用户克隆我们的存储库并在我们的代码中更改路由,可以说是你能想象到的最糟糕的用户体验之一)。

如果这是一个 Web 浏览器,我们就可以写成这样

<a href="details.html">Go to Details</a>

另一种写法是

<a
onClick={() => {
window.location.href = 'details.html';
}}
>
Go to Details
</a>

我们将做类似于后者的事情,但我们不会使用全局的 window.location,而是使用在我们的屏幕组件中可访问的 navigation 对象。

import * as React from 'react';
import { View, Text } from 'react-native';
import {
createStaticNavigation,
useNavigation,
} from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { Button } from '@react-navigation/elements';

function HomeScreen() {
const navigation = useNavigation();

return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button onPress={() => navigation.navigate('Details')}>
Go to Details
</Button>
</View>
);
}

// ... other code from the previous section
Snack 上尝试

让我们分解一下

  • navigation - navigation 对象是从 useNavigation Hook 返回的(更多信息请参见 “深入了解 navigation 对象”)。
  • navigate('Details') - 我们调用 navigate 函数(在 navigation 对象上 — 命名真难!)并传入我们想要用户移动到的路由的名称。
注意

如果我们使用在导航器中未定义的路由名称调用 navigation.navigate,它将在开发构建中打印错误,而在生产构建中则不会发生任何事情。换句话说,我们只能导航到在我们的导航器上定义的路由——我们不能导航到任意组件。

所以我们现在有一个带有两个路由的堆栈:1) Home 路由 2) Details 路由。如果我们再次从 Details 屏幕导航到 Details 路由,会发生什么?

function DetailsScreen() {
const navigation = useNavigation();

return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
<Button onPress={() => navigation.navigate('Details')}>
Go to Details... again
</Button>
</View>
);
}
Snack 上尝试

如果您运行此代码,您会注意到当您点击“再次进入 Details...”时,它什么也不做!这是因为我们已经在 Details 路由上了。navigate 函数大致意思是“转到此屏幕”,如果您已经在该屏幕上,那么它什么也不做是有道理的。

假设我们实际上想要添加另一个详情屏幕。这在您将一些独特数据传递到每个路由的情况下非常常见(稍后当我们讨论 params 时会详细介绍!)。为此,我们可以将 navigate 更改为 push。这允许我们表达添加另一个路由的意图,而无需考虑现有的导航历史记录。

<Button onPress={() => navigation.push('Details')}>
Go to Details... again
</Button>
Snack 上尝试

每次调用 push,我们都会向导航堆栈添加一个新路由。当您调用 navigate 时,如果您尚未在该路由上,它才会推送新路由。

返回

当可以从活动屏幕返回时(如果导航堆栈中只有一个屏幕,则没有什么可以返回的,因此没有返回按钮),原生堆栈导航器提供的头部会自动包含一个返回按钮。

有时您希望能够以编程方式触发此行为,为此,您可以使用 navigation.goBack()

function DetailsScreen() {
const navigation = useNavigation();

return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
<Button onPress={() => navigation.push('Details')}>
Go to Details... again
</Button>
<Button onPress={() => navigation.goBack()}>Go back</Button>
</View>
);
}
Snack 上尝试
注意

在 Android 上,React Navigation 挂钩到硬件返回按钮,并在用户按下它时为您触发 goBack() 函数,因此它的行为符合用户的预期。

另一个常见的需求是能够返回多个屏幕——例如,如果您在堆栈中深入了几个屏幕,并且想要关闭所有屏幕以返回到第一个屏幕。在这种情况下,我们知道我们想要返回到 Home,因此我们可以使用 popTo('Home')。另一种选择是 navigation.popToTop(),它返回到堆栈中的第一个屏幕。

function DetailsScreen() {
const navigation = useNavigation();

return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
<Button onPress={() => navigation.push('Details')}>
Go to Details... again
</Button>
<Button onPress={() => navigation.goBack()}>Go back</Button>
<Button onPress={() => navigation.popTo('Home')}>Go to Home</Button>
<Button onPress={() => navigation.popToTop()}>
Go back to first screen in stack
</Button>
</View>
);
}
Snack 上尝试

总结

  • 如果您尚未在该路由上,navigation.navigate('RouteName') 会将新路由推送到原生堆栈导航器。
  • 我们可以根据需要多次调用 navigation.push('RouteName'),它将继续推送路由。
  • 头部栏将自动显示一个返回按钮,但您可以通过调用 navigation.goBack() 以编程方式返回。在 Android 上,硬件返回按钮可以按预期工作。
  • 您可以使用 navigation.popTo('RouteName') 返回到堆栈中的现有屏幕,并且可以使用 navigation.popToTop() 返回到堆栈中的第一个屏幕。
  • navigation 对象通过 useNavigation Hook 提供给所有屏幕组件。