还记得我说过“稍后我们再谈params!”时说的更多吗?嗯,时候到了。
现在我们知道如何创建一个带有一些路由的Native stack Navigator并在这些路由之间跳转,让我们看看当我们导航到路由时是怎么将数据传递给路由里面的。
一共有两个部分:

  • 通过将参数作为函数的第二个参数放入对象中,将参数传递给路由navigation.navigatenavigation.navigate('RouteName', { /* params go here */ })
  • 跳转后的屏幕组件中的参数(接收参数):route.params.

    我们建议您传递的参数是 可序列化JSON数据。这样,您将能够使用状态持久性,并且您的屏幕组件将拥有实现深度链接的正确契约。

  1. function HomeScreen({ navigation }) {
  2. return (
  3. <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
  4. <Text>Home Screen</Text>
  5. <Button
  6. title="Go to Details"
  7. onPress={() => {
  8. /* 1. Navigate to the Details route with params */
  9. navigation.navigate('Details', {
  10. itemId: 86,
  11. otherParam: 'anything you want here',
  12. });
  13. }}
  14. />
  15. </View>
  16. );
  17. }
  18. function DetailsScreen({ route, navigation }) {
  19. /* 2. Get the param */
  20. const { itemId, otherParam } = route.params;
  21. return (
  22. <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
  23. <Text>Details Screen</Text>
  24. <Text>itemId: {JSON.stringify(itemId)}</Text>
  25. <Text>otherParam: {JSON.stringify(otherParam)}</Text>
  26. <Button
  27. title="Go to Details... again"
  28. onPress={() =>
  29. navigation.push('Details', {
  30. itemId: Math.floor(Math.random() * 100),
  31. })
  32. }
  33. />
  34. <Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
  35. <Button title="Go back" onPress={() => navigation.goBack()} />
  36. </View>
  37. );
  38. }

image.png

更新参数

屏幕也可以更新他们自身的参数,就像他们可以更新他们的状态(state)一样。navigation.setParams方法允许你更新屏幕的参数。有关详细信息,请参阅API 参考setParams
基本用法:

  1. navigation.setParams({
  2. query: 'someText',
  3. });

注意: 避免使用 setParams更新屏幕选项等title。如果你需要更新选项,请setOptions改用。 参数和选项是不同的。

初始化参数

你还可以将一些初始参数传递给屏幕。如果你在导航到此屏幕时没有指定任何参数,就将使用初始参数。它们也与您传递的任何参数浅合并。可以使用initialParams prop属性指定初始参数:

  1. <Stack.Screen
  2. name="Details"
  3. component={DetailsScreen}
  4. initialParams={{ itemId: 42 }}
  5. />

将参数传递到之前的一个屏幕

参数不仅可用于将某些数据传递到新屏幕,而且还可用于将数据传递到前一个(上一层)屏幕。
例如,假设你有一个带有创建帖子按钮的屏幕,而创建帖子按钮会打开一个新屏幕来创建帖子。创建帖子完成后,你希望将帖子的数据传递回上一个屏幕。
此时,你可以使用navigate方法,该方法的作用就像goBack屏幕已经存在一样。您可以通过paramswithnavigate将数据传回:

  1. function HomeScreen({ navigation, route }) {
  2. React.useEffect(() => {
  3. if (route.params?.post) {
  4. // Post updated, do something with `route.params.post`
  5. // For example, send the post to the server
  6. }
  7. }, [route.params?.post]);
  8. return (
  9. <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
  10. <Button
  11. title="Create post"
  12. onPress={() => navigation.navigate('CreatePost')}
  13. />
  14. <Text style={{ margin: 10 }}>Post: {route.params?.post}</Text>
  15. </View>
  16. );
  17. }
  18. function CreatePostScreen({ navigation, route }) {
  19. const [postText, setPostText] = React.useState('');
  20. return (
  21. <>
  22. <TextInput
  23. multiline
  24. placeholder="What's on your mind?"
  25. style={{ height: 200, padding: 10, backgroundColor: 'white' }}
  26. value={postText}
  27. onChangeText={setPostText}
  28. />
  29. <Button
  30. title="Done"
  31. onPress={() => {
  32. // Pass and merge params back to home screen
  33. // 合并参数回到Home屏幕
  34. navigation.navigate({
  35. name: 'Home',
  36. params: { post: postText },
  37. merge: true,
  38. });
  39. }}
  40. />
  41. </>
  42. );
  43. }

在这里,按下“Done”后,Home屏幕route.params将更新来展示你输入的帖子文本。

将参数传递给嵌套的导航器

如果你有嵌套的导航器,则需要以不同的方式传递参数。例如,假设你在Account屏幕内有一个导航器,并且想要将参数传递给该导航器内的屏幕Settings。然后你可以传递参数如下:

  1. navigation.navigate('Account', {
  2. screen: 'Settings',
  3. params: { user: 'jane' },
  4. });

有关嵌套的更多详细信息,请参阅嵌套导航器。

参数中应该包含什么?

了解 params 中应该包含什么样的数据是非常重要的。
参数就像屏幕的选项,它们应该只包含配置屏幕显示内容的信息。避免传递将显示在屏幕本身上的完整数据(例如传递用户 ID 而不是用户对象)。还要避免传递多个屏幕使用的数据,这些数据应该在全局存储中。
你也可以把路由对象想象成一个 URL。如果您的屏幕有一个 URL,那么 URL 中应该是什么?参数不应包含您认为不应出现在 URL 中的数据。这通常意味着你应该尽可能少的保留确定屏幕是什么所需的数据。想想访问一个购物网站,当你看到产品列表时,URL 通常包含类别名称、排序类型、任何过滤器等,它不包含屏幕上显示的实际产品列表。
例如,假设您有Profile屏幕。导航到它时,您可能想在参数中传递用户对象:

  1. // 重要的事情说三遍: 千万不要这样做!
  2. // 重要的事情说三遍: 千万不要这样做!
  3. // 重要的事情说三遍: 千万不要这样做! 传递一个用户ID就可以了
  4. navigation.navigate('Profile', {
  5. user: {
  6. id: 'jane',
  7. firstName: 'Jane',
  8. lastName: 'Done',
  9. age: 25,
  10. },
  11. });

这看起来很方便,并且让你接收route.params.user不需要任何额外工作即可访问用户对象。
但是,这是一种反模式。例如用户对象之类的数据应该在你的全局存储中而不是导航状态中。否则,你会在多个地方重复相同的数据。这可能会导致错误,例如配置文件屏幕显示过时数据,即使用户对象在导航后已更改。
通过深度链接或在 Web 上链接到屏幕也会出现问题,因为:

  1. URL是屏幕的表示,所以它还需要包含参数,即完整的用户对象,这会使URL变得很长且不可读
  2. 由于用户对象在 URL 中,因此可以传递一个随机用户对象,该用户对象代表不存在的用户,或者配置文件中的数据不正确
  3. 如果用户对象没有被传递,或者格式不正确,这可能会导致崩溃,因为屏幕不知道如何处理它

更好的方法是在 params 中只传递用户的 ID:

  1. navigation.navigate('Profile', { userId: 'jane' });

现在,您可以使用传递的内容userId从全局商店中获取用户。这消除了许多问题,例如过时的数据或有问题的 URL。
参数中应该包含的一些示例是:

  1. 用户 ID、项目 ID 等 ID,例如navigation.navigate('Profile', { userId: 'Jane' })
  2. 当您有项目列表时,用于排序、过滤数据等的参数,例如navigation.navigate('Feeds', { sortBy: 'latest' })
  3. 用于分页的时间戳、页码或光标,例如navigation.navigate('Chat', { beforeTime: 1603897152675 })
  4. 在屏幕上填充输入以组成某些东西的数据,例如navigation.navigate('ComposeTweet', { title: 'Hello world!' })

本质上,在参数中传递识别屏幕所需的最少数据量,对于很多情况,这仅仅意味着传递对象的 ID 而不是传递完整的对象。将您的应用程序数据与导航状态分开。

总结

  • navigatepush接受可选的第二个参数,让您将参数传递给您正在导航的路线。例如:navigation.navigate('RouteName', { paramName: 'value' })。
  • route.params您可以在屏幕内接受参数
  • 您可以使用更新屏幕的参数navigation.setParams
  • 初始参数可以通过initialParams道具传递Screen
  • 参数应该包含显示屏幕所需的最少数据,仅此而已