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

传递参数给路由

还记得我说过“当我们谈论 params 时会详细介绍!”吗? 时候到了。

现在我们知道如何创建一个包含一些路由的堆栈导航器,以及如何在这些路由之间导航,接下来让我们看看如何在导航到路由时传递数据。

这里有两部分内容

  1. 通过将参数放入一个对象中,作为 navigation.navigate 函数的第二个参数,将参数传递给路由:navigation.navigate('RouteName', { /* params go here */ })
  2. 在你的屏幕组件中读取参数:route.params
注意

我们建议你传递的参数是 JSON 可序列化的。 这样,你就可以使用状态持久化,并且你的屏幕组件将具有实现深度链接的正确约定。

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

return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
onPress={() => {
/* 1. Navigate to the Details route with params */
navigation.navigate('Details', {
itemId: 86,
otherParam: 'anything you want here',
});
}}
>
Go to Details
</Button>
</View>
);
}

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

/* 2. Get the param */
const { itemId, otherParam } = route.params;

return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
<Text>itemId: {JSON.stringify(itemId)}</Text>
<Text>otherParam: {JSON.stringify(otherParam)}</Text>
<Button
onPress={
() =>
navigation.push('Details', {
itemId: Math.floor(Math.random() * 100),
})
}
>
Go to Details... again
</Button>
<Button onPress={() => navigation.navigate('Home')}>Go to Home</Button>
<Button onPress={() => navigation.goBack()}>Go back</Button>
</View>
);
}
Snack 上尝试

初始参数

你还可以向屏幕传递一些初始参数。 如果你在导航到此屏幕时没有指定任何参数,则将使用初始参数。 它们也会与你传递的任何参数进行浅合并。 初始参数可以在 initialParams 中指定

{
Details: {
screen: DetailsScreen,
initialParams: { itemId: 42 },
},
}

更新参数

屏幕还可以更新它们的参数,就像它们可以更新它们的状态一样。 navigation.setParams 方法允许你更新屏幕的参数。 有关更多详细信息,请参阅 setParams 的 API 参考

基本用法

navigation.setParams({
itemId: Math.floor(Math.random() * 100),
})
Snack 上尝试
注意

避免使用 setParams 来更新屏幕选项,例如 title 等。 如果你需要更新选项,请使用 setOptions 代替。

将参数传递给上一个屏幕

参数不仅对于将一些数据传递到新屏幕很有用,而且对于将数据传递回上一个屏幕也很有用。 例如,假设你有一个带有“创建帖子”按钮的屏幕,并且该按钮打开一个新屏幕来创建帖子。 创建帖子后,你希望将帖子的数据传递回上一个屏幕。

为了实现这一点,你可以使用 popTo 方法返回到上一个屏幕,并向其传递参数

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

// Use an effect to monitor the update to params
React.useEffect(() => {
if (route.params?.post) {
// Post updated, do something with `route.params.post`
// For example, send the post to the server
alert('New post: ' + route.params?.post);
}
}, [route.params?.post]);

return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button onPress={() => navigation.navigate('CreatePost')}>
Create post
</Button>
<Text style={{ margin: 10 }}>Post: {route.params?.post}</Text>
</View>
);
}

function CreatePostScreen({ route }) {
const navigation = useNavigation();
const [postText, setPostText] = React.useState('');

return (
<>
<TextInput
multiline
placeholder="What's on your mind?"
style={{ height: 200, padding: 10, backgroundColor: 'white' }}
value={postText}
onChangeText={setPostText}
/>
<Button
onPress={() => {
// Pass params back to home screen
navigation.popTo('Home', { post: postText });
}}
>
Done
</Button>
</>
);
}
Snack 上尝试

在这里,在你按下“完成”后,主屏幕的 route.params 将被更新以反映你在 navigate 中传递的帖子文本。

将参数传递给嵌套屏幕

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

navigation.navigate('More', {
screen: 'Settings',
params: { user: 'jane' },
})
Snack 上尝试

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

参数中应该包含什么

参数本质上是屏幕的选项。 它们应该包含显示屏幕所需的最少数据,仅此而已。 如果数据被多个屏幕使用,则它应该在全局存储或全局缓存中。 参数并非为状态管理而设计。

你可以将 route 对象视为 URL。 如果你的屏幕有一个 URL,那么 URL 中应该包含什么? 相同的原则适用于参数。 考虑访问购物网站; 当你看到产品列表时,URL 通常包含类别名称、排序类型、任何过滤器等,而不是屏幕上显示的实际产品列表。

例如,假设你有一个 Profile 屏幕。 导航到它时,你可能会想在参数中传递 user 对象

// Don't do this
navigation.navigate('Profile', {
user: {
id: 'jane',
firstName: 'Jane',
lastName: 'Done',
age: 25,
},
});

这看起来很方便,并且让你无需任何额外的工作即可使用 route.params.user 访问 user 对象。

但是,这是一种反模式。 有很多原因说明这是一个坏主意

  • 相同的数据在多个位置重复。 这可能会导致错误,例如即使在导航后 user 对象已更改,个人资料屏幕仍显示过时的数据。

  • 现在,每个导航到 Profile 屏幕的屏幕都需要知道如何获取 user 对象 - 这增加了代码的复杂性。

  • 屏幕的 URL(Web 上的浏览器 URL 或原生上的深度链接)将包含 user 对象。 这很成问题

    1. 由于 user 对象在 URL 中,因此可以传递一个随机的 user 对象,该对象代表一个不存在或在个人资料中包含不正确数据的用户。
    2. 如果未传递或格式不正确的 user 对象,则可能会导致崩溃,因为屏幕将不知道如何处理它。
    3. URL 可能会变得非常长且难以阅读。

更好的方法是在参数中仅传递用户的 ID

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 更新屏幕的参数
  • 初始参数可以通过 Screen 上的 initialParams 属性传递
  • 参数应包含显示屏幕所需的最少数据,仅此而已