屏幕间的跳转
在上一节中,我们定义了一个包含两个路由(Home 和 Details)的堆栈导航器,但我们还没有学习如何让用户从 Home 导航到 Details(虽然我们确实学会了如何在代码中更改_初始_路由,但是强制用户克隆我们的代码库并修改代码才能看到另一个屏幕,这 可能是最糟糕的用户体验之一)。
如果这是在网页浏览器中,我们可以这样写:
<a href="details.html">Go to Details</a>
另一种写法是:
<a
onClick={() => {
window.location.href = 'details.html';
}}
>
Go to Details
</a>
我们将采用类似于后者的方式,但不使用全局的 window.location,而是使用在屏幕组件中可访问的 navigation 对象。
导航到新屏幕
// codeblock-focus-start
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
// codeblock-focus-end
function DetailsScreen() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
</View>
);
}
const RootStack = createNativeStackNavigator({
initialRouteName: 'Home',
screens: {
Home: HomeScreen,
Details: DetailsScreen,
},
});
const Navigation = createStaticNavigation(RootStack);
export default function App() {
return <Navigation />;
}
让我们来分析一下:
navigation-navigation对象由useNavigationhook 返回(稍后在"深入理解 navigation 对象"中会详细介绍)。navigate('Details')- 我们调用navigate函数(在navigation对象上 — 命名确实很难!),并传入我们想要让用户跳转到的路由名称。
如果我们调用 navigation.navigate 时传入了一个未在导航器中定义的路由名称,在开发版本中会打印错误,而在生产版本中则不会有任何反应。换句话说,我们只能导航到已在导航器中定义的路由 — 我们不能导航到任意组件。
现在我们有了一个包含两个路由的堆栈:1) Home 路由 2) Details 路由。如果我们在 Details 屏幕中再次导航到 Details 路由会发生什么呢?
多次导航到同一屏幕
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>
);
}
// codeblock-focus-start
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>
);
}
// codeblock-focus-end
const RootStack = createNativeStackNavigator({
initialRouteName: 'Home',
screens: {
Home: HomeScreen,
Details: DetailsScreen,
},
});
const Navigation = createStaticNavigation(RootStack);
export default function App() {
return <Navigation />;
}
如果你运行这段代码,你会发现当点击"Go to Details... again"时,什么都没有发生!这是因为我们已经在 Details 路由上了。navigate 函数大致意味着"转到这个屏幕",如果你已经在那个屏幕上,那么它不做任何操作是合理的。
假设我们实际上_想要_添加另一个详情屏幕。这在需要为每个路由传递一些唯一数据的情况下很常见(稍后我们讨论 params 时会详细介绍!)。为此,我们可以将 navigate 改为 push。这样我们就可以表达添加另一个路由的意图,而不用考虑现有的导航历史。
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>
);
}
function DetailsScreen() {
const navigation = useNavigation();
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
// codeblock-focus-start
<Button onPress={() => navigation.push('Details')}>
Go to Details... again
</Button>
// codeblock-focus-end
</View>
);
}
const RootStack = createNativeStackNavigator({
initialRouteName: 'Home',
screens: {
Home: HomeScreen,
Details: DetailsScreen,
},
});
const Navigation = createStaticNavigation(RootStack);
export default function App() {
return <Navigation />;
}
每次调用 push 时,我们都会向导航堆栈添加一个新路由。而当你调用 navigate 时,只有在你还没有在该路由上时才会推入新路由。
返回
当可以从当前屏幕返回时(如果导航堆栈中只有一个屏幕,就没有可以返回的地方,因此也就没有返回按钮),原生堆栈导航器提供的标题栏会自动包含一个返回按钮。
有时你可能想要以编程方式触发这个行为,这时你可以使用 navigation.goBack()。
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>
);
}
// codeblock-focus-start
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>
);
}
// codeblock-focus-end
const RootStack = createNativeStackNavigator({
initialRouteName: 'Home',
screens: {
Home: HomeScreen,
Details: DetailsScreen,
},
});
const Navigation = createStaticNavigation(RootStack);
export default function App() {
return <Navigation />;
}
在 Android 上,React Navigation 会监听硬件返回按钮,当用户按下它时会自动为你调用 goBack() 函数,因此它的行为符合用户的预期。
另一个常见需求是能够返回_多个_屏幕 -- 例如,如果你在堆栈中深入了几个屏幕,想要全部关闭并返回到第一个屏幕。在这种情况下,我们知道要返回到 Home,所以可以使用 popTo('Home')。另一种选择是 navigation.popToTop(),它会返回到堆栈中的第一个屏幕。
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>
);
}
// codeblock-focus-start
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>
);
}
// codeblock-focus-end
const RootStack = createNativeStackNavigator({
initialRouteName: 'Home',
screens: {
Home: HomeScreen,
Details: DetailsScreen,
},
});
const Navigation = createStaticNavigation(RootStack);
export default function App() {
return <Navigation />;
}
总结
navigation.navigate('RouteName')如果你还没有在该路由上,它会向原生堆栈导航器推入一个新路由。- 我们可以根据需要多次调用
navigation.push('RouteName'),它会继续推入路由。 - 标题栏会自动显示返回按钮,但你也可以通过调用
navigation.goBack()以编程方式返回。在 Android 上,硬件返回按钮会按预期工作。 - 你可以使用
navigation.popTo('RouteName')返回到堆栈中的某个已存在的屏幕,也可以使用navigation.popToTop()返回到堆栈中的第一个屏幕。 - 通过
useNavigationhook,所有屏幕组件都可以访问navigation对象。