使用 React-Navigation 的类型安全的 navigationHandler

Posted

技术标签:

【中文标题】使用 React-Navigation 的类型安全的 navigationHandler【英文标题】:Type-safe navigationHandler with React-Navigation 【发布时间】:2020-09-15 00:54:25 【问题描述】:

我的 React Native 应用程序的 Typescript 和 React Navigation 存在问题。 我在我的项目中进行了以下设置,这在使用时为我提供了很棒的帮助和自动完成功能,例如navigation.push().

类型

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

export type RootStackParamList = 
    Home: undefined;
    Content: undefined;
    List: undefined;
;

export type StackNavigationProps = StackNavigationProp<RootStackParamList>;

用法

const navigation = useNavigation<StackNavigationProps>();

但是,我创建了一个导航处理函数,它接受一个参数,该参数被馈送到navigation.push。只要参数的类型是any,它就可以正常工作。

const navigationHandler = useCallback((targetScreen) => 
    navigation.push(targetScreen);
, [navigation]);

但我对any 类型并不满意,我更希望targetScreen 实际上只是在我的RootStackParamList 类型中声明的屏幕之一。我尝试了无数种方法来满足导航对象的push 方法,但我被卡住了。

以下尝试是最成功的,但尚未完全按照应有的方式工作。它确实让我在可用屏幕上自动完成,但push 仍然不开心。

类型:

export type NavigationScreen<T extends RootStackParamList> = 
    [K in keyof T]: K;
[keyof T];

用法:

const navigationHandler = useCallback(
    <T extends RootStackParamList>(targetScreen: NavigationScreen<T>) => 
        navigation.push(targetScreen);
,[navigation]);

悬停错误,我收到以下提示:

(参数) targetScreen: [K in keyof T]: K; [keyof T] 参数 type '[ [K in keyof T]: K; [keyof T]]' 不可分配给 '["Home" | 类型的参数“内容” | "列表"] | [“首页” | “内容” | “列表”,未定义]'。键入 '[ [K in keyof T]: K; [keyof T]]' 是 不可分配给类型 '["Home" | “内容” | “列表”]'。 类型 'keyof T' 不可分配给类型 '"Home" | “内容” | “列表”。 键入'字符串 |号码 |符号' 不可分配给类型 '"Home" | “内容” | “列表”。 类型 'string' 不可分配给类型 '"Home" | “内容” | “列表”。 类型“keyof T”不可分配给类型“列表”。 键入'字符串 |号码 |符号'不可分配给类型'“列表”'。 类型“字符串”不可分配给类型“列表”。

如何让它工作?

【问题讨论】:

我目前没有很好的环境来重现您的问题,但您最有可能正在寻找的是targetScreen: keyof RootStackParamList 【参考方案1】:

如果您只对使用屏幕名称为navigationHandler 函数而不是参数感兴趣,您可以按照@artcorpse 在他对您问题的评论中的建议进行操作并使用keyof RootStackParamList

const navigationHandler = useCallback(
    (targetScreen: keyof RootStackParamList) => 
        navigation.push(targetScreen);
    ,
    [navigation]
);

另一方面,如果您想允许参数,您有两个选择:

选项 A:通用函数

const navigationHandler = useCallback(
    <T extends keyof RootStackParamList>(
        targetScreen: T,
        targetScreenParams?: RootStackParamList[T]
    ) => 
        navigation.push(
            targetScreen as keyof RootStackParamList,
            targetScreenParams
        );
    ,
    [navigation]
);

此选项的好处是路由参数对象形状会根据RootStackParamList 类型中定义的相应路由进行检查。例如,

type RootStackParamList = 
    Default: undefined,
    Home: a: 'a';
    Profile:  userId: string ;
;

navigationHandler('Home', a: 'a'); //OK
navigationHandler('Home', userId: 'a'); //ERROR

选项 B:反转已经存在的类型

const navigationHandler2 = useCallback(
    (
        targetScreen: Parameters<typeof navigation.push>[0],
        targetScreenParams?: Parameters<typeof navigation.push>[1]
    ) => 
        navigation.push(targetScreen, targetScreenParams);
    ,
    [navigation]
);

此选项采用您已定义的现有类型并尝试从navigation.push 方法中提取它们。这里的缺点是您不会得到上述关于路线及其相应参数对象形状的检查。

来源:

API documentation for navigation.push Typescript recommendations for react-navigation

【讨论】:

感谢您的精彩回答。我正在学习 TypeScript,但对它不太熟悉——但我发现这个解决方案既优雅又直接。好了,100 + 正确答案 + 点赞的赏金都是你的。 你能详细解释一下选项A> 泛型函数 const navigationHandler 怎么用?

以上是关于使用 React-Navigation 的类型安全的 navigationHandler的主要内容,如果未能解决你的问题,请参考以下文章

我需要下载 react-navigation 并使用命令 npm i @react-navigation/native 并显示错误

react-navigation v5 中的 Typescript StackNavigatonProps 和 Screen 道具

使用 react-navigation 更改状态栏颜色

使用`react-navigation`导航时如何触发render()方法?

如何使用 react-navigation 推送新场景?

使用 react-navigation 时不发送 redux 状态