如何在 React Navigation 中正确输入 useNavigation?

Nix*_*Nix 19 typescript react-native react-navigation react-navigation-stack

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

根据文档,我理解实现应该如下所示:

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!
Run Code Online (Sandbox Code Playgroud)

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

navigation.navigate('Home', { foo: 'hello', onBar: () => {} })
Run Code Online (Sandbox Code Playgroud)

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

这是一个重现 TypeError 的 CodeSandBox

React 导航 types.d.ts链接

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

小智 14

使用 Typescript 时,整个导航应严格键入。值得庆幸的是,我们不需要为它们的实现编写包装器,因为我们提供了泛型类型

我发现向useNavigation挂钩添加类型的最简单方法如下:

RootStackParamList如果您的路由采用参数,则需要更改类型实现

// src/_app.tsx
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";

import HomeScreen from "./screens/home";
import AuthScreen from "./screens/auth";

export type ScreenNames = ["Home", "Auth"] // type these manually
export type RootStackParamList = Record<ScreenNames[number], undefined>;
export type StackNavigation = NavigationProp<RootStackParamList>;

const Stack = createNativeStackNavigator<RootStackParamList>();

export const App = () => {
  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName="Home">
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Auth" component={AuthScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
};
Run Code Online (Sandbox Code Playgroud)
// src/screens/home.tsx
import { useNavigation } from '@react-navigation/native';
import { type StackNavigation } from "../_app";

const HomeScreen = () => {
  const { navigate } = useNavigation<StackNavigation>();

  const handleOnNavigate = () => navigate("Home");

  // ... the rest of the component code
}

export default HomeScreen;
Run Code Online (Sandbox Code Playgroud)

另请注意,它们不使用useNavigation页面/屏幕的钩子,而是使用深层嵌套的组件,因为导航器已经作为 prop 传递给传递到 的组件Stack.Screen,这意味着您还可以通过HomeScreen以下方式使用 的导航器:

// src/screens/home.tsx
import { type StackNavigation } from "../_app";

interface HomeScreenProps {
  navigation: StackNavigation;
}

const HomeScreen: React.FC<HomeScreenProps> = ({ navigation }) => {
  const handleOnNavigate = () => navigation.navigate("Home");

  // ... the rest of the component code
}

export default HomeScreen;
Run Code Online (Sandbox Code Playgroud)


cap*_*ian 5

为了使其仅适用于一个参数,您需要添加| undefined.

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

考虑这个声明:

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

Run Code Online (Sandbox Code Playgroud)

这是最有趣的部分: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"

Run Code Online (Sandbox Code Playgroud)

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

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

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


sat*_*164 3

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

您在类型中指定了这一点:

Home: { foo: string, onBar: () => void }
Run Code Online (Sandbox Code Playgroud)

这意味着Home需要这些参数。如果您的路线不采用任何参数并且您可以这样做navigate('Home'),则不应在类型中指定任何参数。

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

Home: { foo: string, onBar: () => void } | undefined
Run Code Online (Sandbox Code Playgroud)