传递参数给路由
还记得我说过“当我们谈论 params
时会详细介绍!”吗? 时候到了。
现在我们知道如何创建一个包含一些路由的堆栈导航器,以及如何在这些路由之间导航,接下来让我们看看如何在导航到路由时传递数据。
这里有两部分内容
- 通过将参数放入一个对象中,作为
navigation.navigate
函数的第二个参数,将参数传递给路由:navigation.navigate('RouteName', { /* params go here */ })
- 在你的屏幕组件中读取参数:
route.params
。
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>
);
}
初始参数
你还可以向屏幕传递一些初始参数。 如果你在导航到此屏幕时没有指定任何参数,则将使用初始参数。 它们也会与你传递的任何参数进行浅合并。 初始参数可以在 initialParams
中指定
- 静态
- 动态
{
Details: {
screen: DetailsScreen,
initialParams: { itemId: 42 },
},
}
<Stack.Screen
name="Details"
component={DetailsScreen}
initialParams={{ itemId: 42 }}
/>
更新参数
屏幕还可以更新它们的参数,就像它们可以更新它们的状态一样。 navigation.setParams
方法允许你更新屏幕的参数。 有关更多详细信息,请参阅 setParams
的 API 参考。
基本用法
navigation.setParams({
itemId: Math.floor(Math.random() * 100),
})
避免使用 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>
</>
);
}
在这里,在你按下“完成”后,主屏幕的 route.params
将被更新以反映你在 navigate
中传递的帖子文本。
将参数传递给嵌套屏幕
如果你有嵌套导航器,则需要以稍微不同的方式传递参数。 例如,假设你在 More
屏幕内有一个导航器,并且想要将参数传递给该导航器内的 Settings
屏幕。 然后你可以按如下方式传递参数
navigation.navigate('More', {
screen: 'Settings',
params: { user: 'jane' },
})
有关嵌套的更多详细信息,请参阅嵌套导航器。
参数中应该包含什么
参数本质上是屏幕的选项。 它们应该包含显示屏幕所需的最少数据,仅此而已。 如果数据被多个屏幕使用,则它应该在全局存储或全局缓存中。 参数并非为状态管理而设计。
你可以将 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 对象。 这很成问题
- 由于 user 对象在 URL 中,因此可以传递一个随机的 user 对象,该对象代表一个不存在或在个人资料中包含不正确数据的用户。
- 如果未传递或格式不正确的 user 对象,则可能会导致崩溃,因为屏幕将不知道如何处理它。
- URL 可能会变得非常长且难以阅读。
更好的方法是在参数中仅传递用户的 ID
navigation.navigate('Profile', { userId: 'jane' });
现在,你可以使用传递的 userId
从你的全局存储中获取用户。 这消除了许多问题,例如过时的数据或有问题的 URL。
参数中应该包含的一些示例是
- ID,例如用户 ID、项目 ID 等,例如
navigation.navigate('Profile', { userId: 'Jane' })
- 当你有一个项目列表时,用于排序、过滤数据等的参数,例如
navigation.navigate('Feeds', { sortBy: 'latest' })
- 用于分页的时间戳、页码或游标,例如
navigation.navigate('Chat', { beforeTime: 1603897152675 })
- 用于填充屏幕上输入的以撰写某些内容的数据,例如
navigation.navigate('ComposeTweet', { title: 'Hello world!' })
本质上,传递在参数中标识屏幕所需的最少数据,在很多情况下,这仅仅意味着传递对象的 ID 而不是传递完整的对象。 使你的应用程序数据与导航状态分开。
总结
navigate
和push
接受一个可选的第二个参数,让你能够将参数传递给你正在导航到的路由。 例如:navigation.navigate('RouteName', { paramName: 'value' })
。- 你可以在屏幕内通过
route.params
读取参数 - 你可以使用
navigation.setParams
更新屏幕的参数 - 初始参数可以通过
Screen
上的initialParams
属性传递 - 参数应包含显示屏幕所需的最少数据,仅此而已