如何使用 Typescript 构建 React Native 导航以进行属性传递

Posted

技术标签:

【中文标题】如何使用 Typescript 构建 React Native 导航以进行属性传递【英文标题】:How to structure React Native navigation for property passing with Typescript 【发布时间】:2021-05-22 11:57:51 【问题描述】:

我有一个简单的 React Native 应用程序,我正在使用 Typescript 组合在一起,我正在尝试了解如何使导航正常工作。

我是一个内容部分以及选择一个在WebView中打开所选文章时的列表。我无法理解的是我应该如何将 webview 的 URL 传递到包含它的视图中。导航列表和 Web 视图在被选中时都应该填满屏幕。

就目前情况而言,我的基本组件是GuidanceNavigation,其中包含Stack.NavigatorStack.Screen 用于列出所有主题的NavigationItems 视图,Stack.Screen 用于InstructionView,其中包含Web 视图显示说明。

就目前的情况而言,这些都是React.Component 类型,我不清楚的部分是当我在NavigationItems 视图中时,我选择了一个选项,我需要父视图来替换NavigationItems 视图InstructionView,为InstructionView 提供所选文章的标题和文件位置。据我所知,React Navigation 做了一些涉及传递一些导航属性的魔法,但我无法弄清楚这应该如何在 Typescript 上下文中工作,或者我如何通过 Stack Navigator 传递我的类型化对象,甚至是否Stack Navigator 是在这种情况下使用的正确组件。 Typescript-specific documentation 谈论通过导航属性,但没有解释这与 navigator 对象之间的关系,该对象似乎在 javascript 中可用于实际执行导航。

我的代码看起来像这样,自从我使用 React 和我第一次使用 react-native 以来已经有一段时间了,所以它可能既不优秀也不惯用:

GuidanceNavigation.tsx:

const Stack = createStackNavigator();

export class GuidanceNavigation extends React.Component<, >  

  render(): JSX.Element 
    return (
      <SafeAreaView style=styles.container >
        <NavigationContainer>
          <Stack.Navigator>
            <Stack.Screen name="Choose a topic" component="NavigationItems" />
            <Stack.Screen name="Instructions" component="InstructionView" />
      </Stack.Navigator>
      </NavigationContainer>
      </SafeAreaView>
    );
  ;


NavigationTypes.ts:

export interface IFileLink 
    title: string,
    file: string

NavigationItems.tsx:

import  IFileLink  from './NavigationTypes';

export class NavigationItems extends React.Component<, >  

  itemId: number = 0;
  
  readonly items = [
    
      section: R.strings.menu_steps,
      values: [
     title: R.strings.menu_getting_started, file: "./assets/html/getting-started.html" ,
     // and so on

      ]
    ,
    
      section: R.strings.menu_guidance,
      values: [
     title: R.strings.menu_glossary, file: "./assets/html/glossary.html" ,
        // etc
      ]
    
  ];

  itemList(itemList: Array<IFileLink>): JSX.Element[] 
    return itemList.map((it) => this.item(it));
  ;

  navigateToFile(link:IFileLink) 
    console.log("Navigation to "+link.title);
    // This does not work because navigation comes from ??? somewhere???
    navigation.navigate('Instructions', link);
  ;

  item(fileLink:IFileLink): JSX.Element 
    return(
      <Button
    key=  this.itemId++ 
    title=  fileLink.title 
    onPress= () =>
      this.navigateToFile(fileLink)
    
      />
    );
  ;

  render(): JSX.Element 
    return (
      <ScrollView>
          <Text style=styles.sectionTitle>this.items[0].section</Text>
          this.itemList(this.items[0].values)
          <Text style=styles.sectionTitle>this.items[1].section</Text>
          this.itemList(this.items[1].values)
      </ScrollView>
    )
  ;


InstructionView.tsx:

import  IFileLink  from './NavigationTypes';

export class InstructionView extends React.Component<IFileLink> 

  private fileLink: IFileLink;

  constructor(fl: IFileLink) 
    super(fl);
    this.fileLink = fl;
  

  render(): JSX.Element 
    return (
        <WebView
          source= uri: this.fileLink.file 
          />
    );
  

当在该列表中选择项目时,如何确保使用所需属性从 NavigationItems 导航到 InstructionView

【问题讨论】:

【参考方案1】:

这不起作用,因为navigation 来自???某处???

navigation 对象来自组件的props。当你的组件通过 Stack.Screen 加载时,react-navigation 会使用 props routenavigation 调用组件。

在打字稿方面,您需要声明 NavigationItems 组件 必须 使用此 navigation 属性调用。你如何做到这一点就是你在文档中看到的解释。

您声明一个 RootStackParamList 类型,将您的屏幕名称映射到所需的道具。

type RootStackParamList = 
    ['Choose a topic']: undefined; // no required props
    Instructions: IFileLink; // requires an IFileLink as props
;

您在创建Stack 实例时将此类型用作泛型。

const Stack = createStackNavigator<RootStackParamList>();

当您为NavigationItems 组件声明navigation 属性的类型时,您再次将此类型用作泛型。现在你的组件知道它会有一个prop navigate,所以你可以调用this.props.navigate。它还知道有关您可以导航到的位置的非常详细的信息。屏幕名称必须是'Choose a topic''Instructions',否则会出现错误。它还知道所有屏幕的独特道具要求,因此如果您尝试导航到 Instructions 而不传递 IFileLink 作为第二个参数,则会出现错误。

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

type ItemsProps = 
    navigation: StackNavigationProp<RootStackParamList, 'Choose a topic'>;
;

export class NavigationItems extends React.Component<ItemsProps, >  

    navigateToFile(link: IFileLink) 
        console.log("Navigation to " + link.title);
        // can now navigate via props
        this.props.navigation.navigate('Instructions', link);
    ;

    /*...*/

您的Navigator 中有一个错误。您需要传递组件本身,而不是它们的string 名称。

<Stack.Screen name="Choose a topic" component=NavigationItems />
<Stack.Screen name="Instructions" component=InstructionView />

Typescript Playground Link

【讨论】:

以上是关于如何使用 Typescript 构建 React Native 导航以进行属性传递的主要内容,如果未能解决你的问题,请参考以下文章

使用 create-react-app 和 Typescript 时如何在构建时加载 .md 文件?

如何将 CSS 模块与 Typescript、React 和 Webpack 一起使用?

在 React 和 TypeScript 中构建布局组件

typescript+reactjs+requirejs:如何将 react-dom 导入 typescript

如何使用 Typescript、React 和 Webpack 导入 SCSS 或 CSS 模块

TypeScript 和 React:如何重载高阶组件/装饰器?