在React Native中使用TypeScript的实例

866 阅读8分钟

毫无疑问,TypeScript已经成为许多开发者的最爱,在npm每周有超过1900万次的下载,你有可能在每三个与JavaScript相关的工作描述中看到Typescript。

在这篇文章中,我们将研究如何在你的React Native应用程序中使用TypeScript。在本文结束时,你会很自如地利用TypeScript的优势来减少错误,并在你的应用程序中添加类型检查。

什么是TypeScript?

TypeScript是微软在JavaScript基础上建立的一种开源语言。你可以把 TypeScript 看作是 JavaScript,但有静态类型定义。

为什么你应该考虑 TypeScript

如果本文的前两段还不足以说服你尝试 TypeScript,这里还有几个原因:

  • 更好的文档。有了明确定义的类型,你可以很容易地知道如何引用你的代码的其他部分
  • 减少错误。TypeScript可以在执行前验证你的代码,因此可以节省大量时间来调试为什么 "undefined不是第23行的一个函数"💀
  • 编译成JavaScript。因为TypeScript是JavaScript的超集,它可以编译为JavaScript,所有有效的JavaScript都是有效的TypeScript。
  • 易于采用。如果你曾经想把TypeScript添加到你现有的JavaScript应用程序中,你可以轻松地从一个文件开始,看看它是如何发展的

用React Native和TypeScript构建一个应用程序

现在我们知道了TypeScript是什么,我们可以开始研究如何在我们的React Native应用程序中使用它。

在本教程中,我们将建立一个购物清单应用程序。这是一个很好的方法,可以看到TypeScript如何改善你的开发经验,而不会让事情变得太复杂。

要求

  • 运行CLI命令的基本知识
  • 在你的机器上安装Node.js和npm
  • 在你的机器上安装XCode或Android Studio
  • 具备JavaScript的基本知识
  • React的基本知识
  • 对React Native有一定的经验(建议,不是必须)。

开始工作

让我们开始吧,好吗?在你的终端运行下面的命令。

npx react-native init ShoppingList --template react-native-template-typescript

上面的命令将建立一个新的React Native项目,该项目使用TypeScript模板,在一个名为ShoppingList 的文件夹中包含所需的依赖和配置。

要了解如何在现有的应用程序中使用TypeScript,请看一下文档

构建基础UI

在你的代码编辑器中打开该文件夹,并运行下面的命令(取决于你的机器)来启动该项目。

# MacOS
npm run ios

# Windows/Linux
npm run android

App.tsx 组件是我们应用程序中的根组件。这个组件将包含我们在应用程序中使用的其他组件,我们将在稍后回到它。现在,让我们来创建我们的第一个组件。

在根目录下创建一个类似src/components 的文件夹结构,并在components 文件夹中创建一个Header.tsx 组件。

你是否注意到,我们创建的文件有一个.tsx 的扩展名?那是因为我们现在是用TypeScript构建,我们需要使用适当的文件扩展名,否则我们会在普通的.js 文件中出现错误。

将下面的代码粘贴到文件中。

import React from 'react';
import {View, Text, StyleSheet} from 'react-native';
interface Props {
  title: string;
}
const Header: React.FC<Props> = ({title}) => {
  return (
    <View style={styles.header}>
      <Text style={styles.headerText}>{title}</Text>
    </View>
  );
};
const styles = StyleSheet.create({
  header: {
    paddingVertical: 20,
    borderBottomWidth: 1,
    borderBottomColor: '#cfcfcf',
    alignItems: 'center',
  },
  headerText: {
    fontSize: 20,
    fontWeight: '600',
  },
});
export default Header;

如果你已经熟悉了React Native,请忽略上面的大部分代码;你会知道我们在这里要做什么。然而,我想提请你注意第3-6行。

interface Props {
  title: string;
}
const Header: React.FC<Props> = ({title}) => { /* content here*/}

因为我们使用的是TypeScript,我们现在能够定义我们的组件应该接受什么,应该如何接受,以及接受多少。

在前三行中,我们声明了一个接口,它是我们通常在组件中访问的props 对象的结构。然而,这一次我们使用TypeScript来指定道具和它们的类型。

这样做的好处是,我们可以得到更好的IntelliSense和一些验证,当我们使用该组件时(例如,当title 没有传递给该组件时,会有一个即时错误)。

回到App.tsx 组件中,用下面的代码替换内容:

import React from 'react';
import {SafeAreaView, StyleSheet, Text, View} from 'react-native';
import Header from './src/components/Header';
const App = () => {
  return (
    <SafeAreaView style={styles.container}>
      <Header />
    </SafeAreaView>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#e8e7e3',
  },
});
export default App;

你应该注意到在<Header/> 组件下面有一条红线。如果你把鼠标悬停在它上面,你应该看到一条信息,即该组件期望有一个title ,但没有提供这个道具。现在让我们来做这件事。

用下面的代码片断替换这一行,你应该看到错误已经消失了。

<Header title="Shopping List" />

如果你试图分配一个数字(或任何其他不是字符串的数据类型),你会得到一个不同的错误。这是TypeScript试图帮助你在错误发生之前抓住它们。

将项目添加到列表中

创建一个新的组件,AddItem.tsx ,到你的/src/components 文件夹,然后粘贴下面的代码:

import React, {useState} from 'react';
import {
  View,
  Text,
  TextInput,
  TouchableOpacity,
  StyleSheet,
} from 'react-native';
export interface IItem {
  item: string;
  quantity: string;
}
const AddItem = () => {
  const [item, setItem] = useState('');
  const [quantity, setQuantity] = useState('');
  return (
    <View>
      <Text style={styles.heading}>Add Shopping Item</Text>
      <View style={styles.form}>
        <TextInput
          style={styles.input}
          placeholder="Enter item"
          value={item}
          onChangeText={text => setItem(text)}
        />
        <TextInput
          style={styles.input}
          placeholder="Enter quantity"
          keyboardType="numeric"
          value={quantity}
          onChangeText={q => {
            setQuantity(q);
          }}
        />
        <TouchableOpacity style={styles.addItemButton} onPress={() => {}}>
          <Text style={styles.buttonText}>Add Item</Text>
        </TouchableOpacity>
      </View>
    </View>
  );
};
const styles = StyleSheet.create({
  heading: {
    fontSize: 20,
    fontWeight: '700',
  },
  form: {
    marginTop: 30,
  },
  input: {
    padding: 15,
    borderColor: 'rgba(0, 0, 0, 0.2)',
    borderWidth: 1,
    borderRadius: 5,
    marginBottom: 20,
  },
  addItemButton: {
    backgroundColor: '#eb8634',
    paddingVertical: 20,
    borderRadius: 5,
    alignItems: 'center',
  },
  buttonText: {color: '#fff', fontWeight: '500'},
});
export default AddItem;

注意到第9行中命名的导出IItem ?那是我们购物清单中的一个项目的结构,我们导出它是因为我们将在其他组件中需要它。

回到App.tsx ,用下面的代码更新该组件:

import React, {useState} from 'react';
import {SafeAreaView, StyleSheet, Text, View} from 'react-native';
import Header from './src/components/Header';
import AddItem, {IItem} from './src/components/AddItem'; /* import AddItem and interface*/
const App = () => {
  const [shoppingList, setShoppingList] = useState<IItem[]>([]); // set the type of what the hook expects to be an array of IItems.
  return (
    <SafeAreaView style={styles.container}>
      <Header title="Shopping List" />
      <View style={styles.contentWrapper}>
        <AddItem
          setShoppingList={setShoppingList}
          shoppingList={shoppingList}
        />
      </View>
    </SafeAreaView>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#e8e7e3',
  },
  contentWrapper: {
    padding: 20,
  },
});
export default App;

现在在App.tsx ,我们导入了新的AddItem 组件和IItem 接口,所以我们可以使用(在第6行)useState 钩子来创建shoppingList 状态。

我们指定在使用setShoppingList 函数时,钩子应该接受一个IItem 的数组。如果你把鼠标悬停在setShoppingList 函数上,你会看到这个类型。记下它,当我们在AddItem.tsx 中向购物清单添加物品时,我们很快就会需要它了。

回到AddItem.tsx 组件,用这个更新它:

import React, {useState} from 'react';
import {
  View,
  Text,
  TextInput,
  TouchableOpacity,
  StyleSheet,
  Alert,
} from 'react-native';
export interface IItem {
  item: string;
  quantity: string;
}
interface Props {
  setShoppingList: React.Dispatch<React.SetStateAction<IItem[]>>;
  shoppingList: IItem[];
}
const AddItem: React.FC<Props> = ({shoppingList, setShoppingList}) => {
  const [item, setItem] = useState('');
  const [quantity, setQuantity] = useState('');
  const addItem = () => {
    if (!item) {
      Alert.alert('No Item!', 'You need to enter an item');
    } else {
      setShoppingList([
        ...shoppingList,
        {
          item,
          quantity: quantity || '1',
        },
      ]);
      setItem('');
      setQuantity('');
    }
  };
  return (
    <View>
      <Text style={styles.heading}>Add Shopping Item</Text>
      <View style={styles.form}>
        <TextInput
          style={styles.input}
          placeholder="Enter item"
          value={item}
          onChangeText={text => setItem(text)}
        />
        <TextInput
          style={styles.input}
          placeholder="Enter quantity"
          keyboardType="numeric"
          value={quantity}
          onChangeText={q => {
            setQuantity(q);
          }}
        />
        <TouchableOpacity style={styles.addItemButton} onPress={addItem}>
          <Text style={styles.buttonText}>Add Item</Text>
        </TouchableOpacity>
      </View>
    </View>
  );
};
const styles = StyleSheet.create({
  heading: {
    fontSize: 20,
    fontWeight: '700',
  },
  form: {
    marginTop: 30,
  },
  input: {
    padding: 15,
    borderColor: 'rgba(0, 0, 0, 0.2)',
    borderWidth: 1,
    borderRadius: 5,
    marginBottom: 20,
  },
  addItemButton: {
    backgroundColor: '#eb8634',
    paddingVertical: 20,
    borderRadius: 5,
    alignItems: 'center',
  },
  buttonText: {color: '#fff', fontWeight: '500'},
});
export default AddItem;

该组件现在有了很大的变化,所以我将带领你完成这些变化。

在第14行,我们定义了Props 接口并设置了每个属性的类型,然后在第18行将其设置为我们的功能组件props 的类型。

我们还在第21行创建了一个处理函数,当 "添加项目 "按钮被点击时,该函数检查是否有项目被输入到字段中,然后调用setShoppingList() 函数,向列表中添加一个新项目。

列出项目

现在我们已经能够添加新的项目,让我们创建一个组件来列出它们。

/src/components 中创建另一个文件,命名为Item.tsx ,并在里面粘贴下面的代码:

import React from 'react';
import {View, Text, StyleSheet} from 'react-native';
import {IItem} from './AddItem';
const Item: React.FC<IItem> = ({item, quantity}) => {
  return (
    <View style={styles.item}>
      <Text style={styles.itemName}>{item}</Text>
      <Text style={styles.quantity}>x{quantity}</Text>
    </View>
  );
};
const styles = StyleSheet.create({
  item: {
    padding: 20,
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    borderBottomWidth: 1,
    borderBottomColor: 'rgba(0, 0, 0, 0.2)',
  },
  itemName: {
    fontWeight: '500',
  },
  quantity: {
    padding: 6,
    borderWidth: 1,
    borderColor: 'rgba(0, 0, 0, 0.2)',
    borderRadius: 10,
    overflow: 'hidden',
    backgroundColor: 'rgba(0, 0, 0, 0.05)',
  },
});
export default Item;

你可能已经掌握了它的窍门!Item.tsx 组件接受来自IItem 的属性作为道具,然后我们用一些样式来渲染它们。

现在,我们需要通过将该组件导入App.tsx ,并使用内置的 [FlatList]([https://reactnative.dev/docs/flatlist](https://reactnative.dev/docs/flatlist))组件。

用下面的代码替换App.tsx 中的内容。

import React, {useState} from 'react';
import {SafeAreaView, StyleSheet, View, FlatList} from 'react-native';
import Header from './src/components/Header';
import AddItem, {IItem} from './src/components/AddItem';
import Item from './src/components/Item';
const App = () => {
  const [shoppingList, setShoppingList] = useState<IItem[]>([]);
  return (
    <SafeAreaView style={styles.container}>
      <Header title="Shopping List" />
      <View style={styles.contentWrapper}>
        <AddItem
          setShoppingList={setShoppingList}
          shoppingList={shoppingList}
        />
        <FlatList
          data={shoppingList}
          keyExtractor={(item, index) => `${item.item}-${index}`}
          renderItem={({item}) => (
            <Item item={item.item} quantity={item.quantity} />
          )}
        />
      </View>
    </SafeAreaView>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#e8e7e3',
  },
  contentWrapper: {
    padding: 20,
  },
});
export default App;

我们的应用程序现在已经完成了。FlatList在第16行,我们使用FlatList 组件来渲染我们列表中的项目,你可以看到我们是如何在renderItem 的道具中使用Item 组件的。

结语

这篇文章旨在简单介绍一下你如何在React Native应用程序中受益于TypeScript的功能。购物清单应用足够简单,向你展示了如何使用TypeScript,而不会让事情变得复杂。

你可以通过实现从列表中删除项目的功能,使事情更进一步。