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

bpzcxfmw  于 2022-12-27  发布在  React
关注(0)|答案(4)|浏览(248)

我尝试从React Navigation输入useNavigation。我希望能够只传递路线的名称,但我得到一个错误,除非我也传递该路线的 prop 。
根据文档,我理解实现应该如下所示:

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。如果我为那个路径添加了props,错误就会消失,但是我不应该这样做。

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

看一下navigation.navigate的类型声明(见下文),这应该是不必要的,因为有一个重载只传递路由的名称作为唯一的参数。我一定是做错了什么,因为这是不接受的...但是什么,在哪里,为什么?
Here is a CodeSandBox reproducing the TypeError.

    • React导航类型. 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;
up9lanfz

up9lanfz1#

我在最后一行得到了一个TypeError。如果我为那个路径添加了props,错误就会消失,但是我不应该这样做。
您在类型中指定了此选项:

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

这意味着Home需要这些参数,如果你的路径不需要任何参数,你可以只使用navigate('Home'),你不应该在类型中指定任何参数。
如果这些参数是可选的,则需要相应地指定类型:

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

eeq64g8w2#

在类型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"});
1sbrub3j

1sbrub3j3#

为了使它只使用一个参数,您需要添加| 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不允许只传递一个参数的原因。

tcbh2hod

tcbh2hod4#

当使用Typescript时,整个导航都应该是严格类型化的,谢天谢地,我们不需要为它们的实现编写 Package 器,因为我们提供了generic types
我发现向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>
  );
};
// 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;

还要注意的是,它们不使用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;

相关问题