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

问题描述 投票:0回答:7

我正在尝试从 React Navigation 中输入

useNavigation
。我希望能够仅传递路线名称,但除非我也传递该路线的道具,否则我会收到错误。

按照文档,我理解实现应该如下所示:

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!
我在最后一行收到类型错误。如果我为该路线添加道具,错误就会消失,但我不必这样做。

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

navigation.navigate

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

这是一个重现 TypeError 的 CodeSandBox。

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;


typescript react-native react-navigation react-navigation-stack
7个回答
13
投票
泛型类型

我发现向

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;



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;

这是最有趣的部分:
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 不允许你只传递一个参数。


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

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

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

这意味着 
Home

接受这些参数。如果您的路线不采用任何参数,并且您可以执行

navigate('Home')
,则不应在类型中指定任何参数。
如果这些参数是可选的,那么您需要相应地指定类型:

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



3
投票
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"});



2
投票
您需要做的就是导入所需的钩子、道具和类型,然后像上面的示例一样应用
干杯👋


0
投票
查看注释掉的 NavigationProps 以及它下面可能应该包含的内容。


0
投票
CompositeScreenProps

,这对我来说效果很好。这是示例路线结构。


根路由
  • 主要路线
    • 首页
      聊天路线
    • 授权
    • 电子邮件
      • 电话
  • 这是我想从“电话”转到“家”时的示例代码。

/* RootRoute.tsx */ export type RootRoute = { MainRoute: undefined; ChatRoute: undefined; AuthRoute: undefined; }; const Stack = createStackNavigator<RootRoute>(); const Root = () => { return ( <Stack.Navigator> <Stack.Screen name={'MainRoute'} component={MainRoute} /> <Stack.Screen name={'ChatRoute'} component={ChatRoute} /> <Stack.Screen name={'AuthRoute'} component={AuthRoute} /> </Stack.Navigator> );

/* MainRoute.tsx */
export type MainRoute = {
  Home: undefined;
  ...
};

const Stack = createStackNavigator<MainRoute>();

const Main = () => {
  return (
    <Stack.Navigator>
      <Stack.Screen name={'Home'} component={...} />
      <Stack.Screen name={...} component={...} />
    </Stack.Navigator>
  );
/* AuthRoute.tsx */
export type AuthRoute = {
  Email: undefined;
  Phone: undefined;
};

const Stack = createStackNavigator<AuthRoute>();

const Validation = () => {
  return (
    <Stack.Navigator>
      <Stack.Screen name={'Email'} component={EmailAuthScreen} />
      <Stack.Screen name={'Phone'} component={PhoneAuthScreen} />
    </Stack.Navigator>
  );
/* PhoneAuthScreen.tsx */
interface PhoneValidationProps
  extends StackScreenProps<AuthRoute, 'Phone'> {}

const PhoneValidationScreen = ({ route }: PhoneValidationProps) => {
  const { navigation, route } =
    useNavigation<
      CompositeScreenProps<
        StackScreenProps<RootRoute, 'MainRoute'>,
        StackScreenProps<AuthRoute, 'Phone'>
      >
    >();
  ...
}
© www.soinside.com 2019 - 2024. All rights reserved.