Skip to main content
Version: 6.x

在屏幕之间移动

在上一节 "你好 React 导航" 中,我们定义了一个具有两条路由(HomeDetails)的堆栈导航器,但我们没有学习如何让用户从 Home 导航到 Details(尽管我们确实学习了如何更改我们的初始路由)代码,但强迫我们的用户克隆我们的存储库并更改代码中的路径以便看到另一个屏幕可以说是人们可以想象的最糟糕的用户体验之一)。

¥In the previous section, "Hello React Navigation", we defined a stack navigator with two routes (Home and Details), but we didn't learn how to let a user navigate from Home to Details (although we did learn how to change the initial route in our code, but forcing our users to clone our repository and change the route in our code in order to see another screen is arguably among the worst user experiences one could imagine).

如果这是一个网络浏览器,我们就可以编写如下内容:

¥If this was a web browser, we'd be able to write something like this:

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

另一种写法是:

¥Another way to write this would be:

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

我们将做与后者类似的事情,但我们将使用传递到屏幕组件的 navigation 属性,而不是使用 window.location 全局变量。

¥We'll do something similar to the latter, but rather than using a window.location global, we'll use the navigation prop that is passed down to our screen components.

¥Navigating to a new screen

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

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>
);
}

// ... other code from the previous section

让我们来分解一下:

¥Let's break this down:

  • navigation - navigation 属性被传递到原生堆栈导航器中的每个屏幕组件 (definition)(稍后将在 "深入的导航属性" 中详细介绍)。

    ¥navigation - the navigation prop is passed in to every screen component (definition) in the native stack navigator (more about this later in "The navigation prop in depth").

  • navigate('Details') - 我们用我们想要将用户移动到的路由名称来调用 navigate 函数(在 navigation 属性上 - 命名很困难!)。

    ¥navigate('Details') - we call the navigate function (on the navigation prop — naming is hard!) with the name of the route that we'd like to move the user to.

注意

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

¥If we call navigation.navigate with a route name that we haven't defined in a navigator, it'll print an error in development builds and nothing will happen in production builds. Said another way, we can only navigate to routes that have been defined on our navigator — we cannot navigate to an arbitrary component.

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

¥So we now have a stack with two routes: 1) the Home route 2) the Details route. What would happen if we navigated to the Details route again, from the Details screen?

¥Navigate to a route multiple times

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

如果你运行此代码,你会注意到当你点击“转到详细信息...再次“它没有做任何事情!这是因为我们已经处于“详细信息”路由上。navigate 函数大致意味着 "转到此屏幕",如果你已经在该屏幕上,那么它不会执行任何操作是有道理的。

¥If you run this code, you'll notice that when you tap "Go to Details... again" that it doesn't do anything! This is because we are already on the Details route. The navigate function roughly means "go to this screen", and if you are already on that screen then it makes sense that it would do nothing.

假设我们实际上想要添加另一个详细信息屏幕。当你向每条路由传递一些唯一数据时,这种情况很常见(稍后我们讨论 params 时会详细介绍!)。为此,我们可以将 navigate 更改为 push。这使我们能够表达添加另一条路由的意图,而不管现有的导航历史记录如何。

¥Let's suppose that we actually want to add another details screen. This is pretty common in cases where you pass in some unique data to each route (more on that later when we talk about params!). To do this, we can change navigate to push. This allows us to express the intent to add another route regardless of the existing navigation history.

<Button
title="Go to Details... again"
onPress={() => navigation.push('Details')}
/>

每次你调用 push 时,我们都会向导航堆栈添加一条新路由。当你调用 navigate 时,它首先尝试查找具有该名称的现有路由,并且仅在堆栈中尚无路由时才推送新路由。

¥Each time you call push we add a new route to the navigation stack. When you call navigate it first tries to find an existing route with that name, and only pushes a new route if there isn't yet one on the stack.

回去

¥Going back

当可以从活动屏幕返回时,原生堆栈导航器提供的标题将自动包含一个后退按钮(如果导航堆栈中只有一个屏幕,则没有任何内容可以返回,因此没有后退按钮)。

¥The header provided by the native stack navigator will automatically include a back button when it is possible to go back from the active screen (if there is only one screen in the navigation stack, there is nothing that you can go back to, and so there is no back button).

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

¥Sometimes you'll want to be able to programmatically trigger this behavior, and for that you can use navigation.goBack();.

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')}
/>
<Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
<Button title="Go back" onPress={() => navigation.goBack()} />
</View>
);
}
注意

在 Android 上,React Navigation 钩子到硬件后退按钮,并在用户按下它时为你触发 goBack() 功能,因此它的行为符合用户的预期。

¥On Android, React Navigation hooks in to the hardware back button and fires the goBack() function for you when the user presses it, so it behaves as the user would expect.

另一个常见的要求是能够返回多个屏幕 - 例如,如果你在堆栈中包含多个屏幕,并且想要关闭所有屏幕以返回到第一个屏幕。在本例中,我们知道我们想要返回到 Home,因此我们可以使用 navigate('Home')(而不是 push!尝试一下,看看有什么不同)。另一种选择是 navigation.popToTop(),它返回到堆栈中的第一个屏幕。

¥Another common requirement is to be able to go back multiple screens -- for example, if you are several screens deep in a stack and want to dismiss all of them to go back to the first screen. In this case, we know that we want to go back to Home so we can use navigate('Home') (not push! try that out and see the difference). Another alternative would be navigation.popToTop(), which goes back to the first screen in the stack.

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')}
/>
<Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
<Button title="Go back" onPress={() => navigation.goBack()} />
<Button
title="Go back to first screen in stack"
onPress={() => navigation.popToTop()}
/>
</View>
);
}

概括

¥Summary

  • 如果新路由尚未在堆栈中,则 navigation.navigate('RouteName') 会将新路由推送到原生堆栈导航器,否则会跳转到该屏幕。

    ¥navigation.navigate('RouteName') pushes a new route to the native stack navigator if it's not already in the stack, otherwise it jumps to that screen.

  • 我们可以多次调用 navigation.push('RouteName'),它会继续推送路由。

    ¥We can call navigation.push('RouteName') as many times as we like and it will continue pushing routes.

  • 标题栏将自动显示后退按钮,但你可以通过调用 navigation.goBack() 以编程方式返回。在 Android 上,硬件后退按钮按预期工作。

    ¥The header bar will automatically show a back button, but you can programmatically go back by calling navigation.goBack(). On Android, the hardware back button just works as expected.

  • 你可以使用 navigation.navigate('RouteName') 返回到堆栈中的现有屏幕,并且可以使用 navigation.popToTop() 返回到堆栈中的第一个屏幕。

    ¥You can go back to an existing screen in the stack with navigation.navigate('RouteName'), and you can go back to the first screen in the stack with navigation.popToTop().

  • navigation 属性可用于所有屏幕组件(在路由配置中定义为屏幕并由 React Navigation 呈现为路由的组件)。

    ¥The navigation prop is available to all screen components (components defined as screens in route configuration and rendered by React Navigation as a route).