【问题标题】:How to properly type useNavigation in React Navigation?如何在 React Navigation 中正确键入 useNavigation?
【发布时间】:2021-10-14 17:45:37
【问题描述】:

我正在尝试从 React Navigation 中键入 useNavigation。我希望能够只传递路线的名称,但除非我也传递该路线的道具,否则我会收到错误消息。

the documentation 之后,我知道实现应该是这样的:

import { StackNavigationProp } from '@react-navigation/stack';

type StackParamList = {
    Home: { foo: string, onBar: () => void }
    About: AboutProps
}

type NavigationProps = StackNavigationProp<StackParamList>

const MyComponent = () => {
  const navigation = useNavigation<NavigationProps>()

  const handleOnNavigate = () => navigation.navigate('Home')
  //                                                    ^ TypeError!

我在最后一行得到一个 TypeError。如果我为该路线添加道具,错误就会消失,但我不应该这样做。

navigation.navigate('Home', { foo: 'hello', onBar: () => {} })

查看navigation.navigate 的类型声明(见下文),这应该不是必需的,因为简单地将路由名称作为唯一参数传递存在重载。我一定是做错了什么,因为这是不被接受的......但是什么,在哪里以及为什么?

Here is a CodeSandBox reproducing the TypeError.

React Navigation types.d.ts (link)

navigate<RouteName extends keyof ParamList>(...args: undefined extends ParamList[RouteName]
  ? [screen: RouteName] | [screen: RouteName, params: ParamList[RouteName]]
  : [screen: RouteName, params: ParamList[RouteName]])
: void;

【问题讨论】:

  • 请分享可重现的示例。你从哪里得到useNavigationAboutProps。请在 ts 操场上分享您的示例
  • 我添加了一个 CodeSandBox。
  • 能否提供React Navigation types.d.ts 的链接?我只是看到只有一个参数没有重载
  • 我在 React Navigation 中添加了类型声明的链接。如我所见,第一个三元表达式可以是 screen: RouteName 或相同的加上参数(路由道具)。我可能读错了。

标签: typescript react-native react-navigation react-navigation-stack


【解决方案1】:

我在最后一行得到一个 TypeError。如果我为该路线添加道具,错误就会消失,但我不应该这样做。

你在类型中指定了这个:

Home: { foo: string, onBar: () => void }

这意味着Home 接受这些参数。如果您的路线不使用任何参数并且您可以只使用navigate('Home'),那么您不应该在类型中指定任何参数。

如果这些参数是可选的,那么您需要相应地指定类型:

Home: { foo: string, onBar: () => void } | undefined

【讨论】:

  • 谢谢你,@satya164。我是 React Navigation 的新手,但它的工作方式似乎与针对 Web 浏览器的路由器不同。组件确实有 props,但它们是在父组件中定义并传递给组件的。所以如果我理解正确,如果屏幕需要道具,我总是需要从我导航的任何地方直接传递它们?我应该注意,我当前的实现工作完美 - 它只是报告一个类型错误。
  • 它们不是任何道具,它们是参数。参数用于导航时传递数据。您正在使用initialParams,这对于无需导航即可呈现的主屏幕很有用,但通常这不是参数的主要用例。如果你只想传递一些道具,那么像这样传递:reactnavigation.org/docs/hello-react-navigation/…
  • 啊哈!这是我的困惑;出于某种原因,我认为initialParams 是在 Stack Navigator 中传递道具的惯用方式。我已经用渲染道具替换了它,现在它都按预期工作,没有类型错误。
【解决方案2】:

StackParamList 类型中,您可以定义导航到屏幕时期望的参数类型。如果您不希望 Home 有任何参数,您还必须在您的类型中相应地定义它:

export type StackParamList = {
  Home: undefined,
  About: { bar: string; onBaz: () => void };
};

如果您期望任何额外的参数可选,您可以在类型声明中使用管道:

export type StackParamList = {
  Home: { foo: string; onBar: () => void } | undefined;
  About: { bar: string; onBaz: () => void };
};

// Will work in both ways: 

const handleOnNavigate = () => navigation.navigate('Home')

作为一种解决方法,您还可以使用另一个重载并传递一个配置对象:

const handleOnNavigate = () => navigation.navigate({key : "Home"});

【讨论】:

  • 感谢您提供此答案。这是正确的,但我接受了@satya164 的回答,因为他们是第一个回答的。
【解决方案3】:

为了使它只使用一个参数,您需要添加| undefined

让我解释一下为什么你需要这样做。

考虑这个声明:

navigate<RouteName extends keyof ParamList>(
    ...args: undefined extends ParamList[RouteName]
      ? [screen: RouteName] | [screen: RouteName, params: ParamList[RouteName]]
      : [screen: RouteName, params: ParamList[RouteName]]
  ): void;

这是最有趣的部分:undefined extends ParamList[RouteName] 这意味着如果undefined 扩展ParamList[RouteName] 你只能使用一个参数。

让我们把它分成更小的例子:

type ParamList = {
  Home: { foo: string; onBar: () => void };
  About: { bar: string; onBaz: () => void };
  Undefined: undefined,
};

type Check<T extends keyof ParamList> = undefined extends ParamList[T] ? 'might be undefined' : 'can not be undefined'

type Result = Check<'Home'> // "can not be undefined"
type Result2 = Check<'Undefined'> // "might be undefined"

您可能已经注意到,如果您提供Home,TS 将需要两个参数,因为ParamList['Home'] 返回的对象不能是undefined

另一方面,undefined 扩展了 ParamList['Undefined'] - 因此 TS 只允许您使用一个参数。

这就是为什么 TS 不允许你只传递一个参数。

【讨论】:

  • 感谢您提供此答案,并逐步完成类型声明 - 以及帮助我​​改进问题本身。 +1!
猜你喜欢
  • 2020-05-22
  • 1970-01-01
  • 2020-10-08
  • 2020-08-30
  • 1970-01-01
  • 1970-01-01
  • 2020-12-30
  • 2021-01-03
  • 1970-01-01
相关资源
最近更新 更多