# 向路由传递参数

还记得我说过 "稍后我们讨论 params 时再谈这个吗"? 好吧,时间到了。

现在我们知道了如何创建一个带有一些路由的堆栈导航器并在这些路由之间导航,让我们看看当导航到路由时如何将数据传递给它们。

这有两个方面:

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

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

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

function DetailsScreen({ route, navigation }) {
  /* 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
        title="Go to Details... again"
        onPress={() =>
          navigation.push("Details", {
            itemId: Math.floor(Math.random() * 100),
          })
        }
      />
      <Button title="Go to Home" onPress={() => navigation.navigate("Home")} />
      <Button title="Go back" onPress={() => navigation.goBack()} />
    </View>
  );
}

# 更新参数

屏幕也可以更新它们的参数,就像它们可以更新它们的状态一样。导航。setParams 方法允许您更新屏幕的参数。更多细节请参考 setParams 的 API 参考。

# 基本用法

navigation.setParams({
  query: "someText",
});

注意:避免使用 setParams 更新屏幕选项,如标题等。如果需要更新选项,请使用 setOptions

# 初始参数

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

<Stack.Screen
  name="Details"
  component={DetailsScreen}
  initialParams={{ itemId: 42 }}
/>

# 向前一个屏幕传递参数

参数不仅用于将一些数据传递给新屏幕,还可以用于将数据传递给前一个屏幕。例如,假设您有一个带有创建帖子按钮的屏幕,创建帖子按钮会打开一个新屏幕来创建帖子。在创建文章之后,您希望将文章的数据传递回前一个屏幕。

要实现这一点,可以使用 navigate 方法,如果屏幕已经存在,该方法的行为类似于 goBack。你可以通过 navigate 传递 params 来传递回数据:

function HomeScreen({ navigation, route }) {
  React.useEffect(() => {
    if (route.params?.post) {
      // Post updated, do something with `route.params.post`
      // For example, send the post to the server
    }
  }, [route.params?.post]);

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

function CreatePostScreen({ navigation, route }) {
  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
        title="Done"
        onPress={() => {
          // Pass and merge params back to home screen
          navigation.navigate({
            name: "Home",
            params: { post: postText },
            merge: true,
          });
        }}
      />
    </>
  );
}

这里,在你按下 “Done” 后, Home 屏幕的 route.params 将被更新,以反映你传入 navigate 的文章文本。

# 向嵌套导航器传递参数

如果您有嵌套的导航器,您需要传递一些不同的参数。例如,假设您在 Account 屏幕中有一个导航器,并希望将参数传递到导航器中的 Settings 屏幕。然后你可以传递参数如下:

navigation.navigate("Account", {
  screen: "Settings",
  params: { user: "jane" },
});

有关嵌套的更多细节,请参见嵌套导航器。

# 参数中应该包含什么

理解什么样的数据应该在参数中是很重要的。参数就像是屏幕的选项。它们应该只包含配置屏幕中显示内容的信息。避免传递将在屏幕上显示的完整数据(例如传递一个用户 id 而不是用户对象)。还要避免传递被多个屏幕使用的数据,这样的数据应该在全局存储中。

您还可以将路由对象视为 URL。如果你的屏幕有一个 URL, URL 中应该有什么?参数不应该包含您认为不应该出现在 URL 中的数据。这通常意味着您应该保留尽可能少的数据来确定屏幕是什么。想象一下访问一个购物网站,当你看到产品列表时,URL 通常包含类别名称、分类类型、任何过滤器等,它不包含屏幕上显示的实际产品列表。

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

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

这看起来很方便,并且允许您使用 route.params.user 访问 user 对象,而不需要任何额外的工作。

然而,这是一种反模式。用户对象等数据应该在全局存储中,而不是在导航状态中。否则,相同的数据会在多个地方重复。这可能导致错误,例如概要文件屏幕显示过时的数据,即使用户对象在导航后已经更改。

通过深度链接或网络链接到屏幕也很成问题,因为:

  • URL 是屏幕的一种表示,所以它也需要包含参数,即完整的用户对象,这可能会使 URL 非常长和不可读
  • 因为用户对象在 URL 中,所以有可能传递一个随机的用户对象来表示一个不存在的用户,或者配置文件中有不正确的数据
  • 如果用户对象没有被传递,或者格式不正确,这可能会导致崩溃,因为屏幕不知道如何处理它
  • 更好的方法是只在参数中传递用户的 ID:

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

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

现在,您可以使用传递的 userId 从全局存储中获取用户。这消除了许多问题,如过时的数据或有问题的 url。

一些应该在 params 中包含什么的例子:

  • id,如用户 id,项目 id 等,例如导航。navigation.navigate('Profile', { userId: 'Jane' })
  • 参数用于排序,过滤数据等,当你有一个项目列表,例如 navigation.navigate('Feeds', { sortBy: 'latest' })
  • 时间戳、页码或用于分页的游标,例如 navigation.navigate('Chat', { beforeTime: 1603897152675 })
  • 用于在屏幕上填写输入以组成某些内容的数据,例如 avigation.navigate('ComposeTweet', { title: 'Hello world!' })

本质上,通过参数传递标识屏幕所需的最少的数据,对于很多情况,这只是意味着传递一个对象的 ID,而不是传递一个完整的对象。将应用程序数据与导航状态分开。

# 总结

  • navigatepush 接受一个可选的第二个参数,让您将参数传递给要导航的路由。例如:navigation.navigate('RouteName', { paramName: 'value' })
  • 您可以通过 route.params 读取屏幕内的参数
  • 你可以用 navigation.setParams 更新屏幕的参数
  • 初始参数可以通过屏幕上的 initialParams 属性传递
  • 参数应该包含显示屏幕所需的最小数据,仅此而已