如何使用 TypeScript 对 React 组件进行静态类型化
概括
在本指南中,您将学习如何使用编译为 JavaScript 的语言TypeScript对React.js组件进行静态类型化。使用 TypeScript 和 React 允许您开发具有可识别 props 和 state 对象的强类型组件,这将确保您的组件的任何用法都经过编译器的类型检查。
本指南假设您已经熟悉 React 和 TypeScript 作为独立的技术,但现在有兴趣将它们一起使用。
引用 React Typings
要访问 React TypeScript 类型,请确保基于 TypeScript 的 React 项目已安装@types/react 包:
npm install @types/react --dev
React typings 包将允许您从TypeScript 能够理解的react模块中导入类型。
首先在 TypeScript 文件中导入 React:
import * as React from "react";
Facebook 官方的create-react-app包开箱即用地支持 TypeScript 。
键入类组件的属性和状态
要实现 React 类组件,需要扩展的类是React.Component<P, S>或React.PureComponent<P, S>。
这些是TypeScript 中的泛型类型,可轻松替换类型。例如,React.Component<P>的简单类型定义如下:
declare class Component<P> {
props: P
}
您可以大声读出此语法为“P 的组件”,其中P将是类定义中引用的 props 类型。P可以是任何名称,没有规则,但常见的命名约定是使用T作为前缀,例如TProps。
对于React.Component<P, S>,每个泛型参数分别对应于 props 和 state 类型。普通组件和纯组件在 TypeScript 用法上没有区别。
以下是ShoppingBasket组件,用于呈现产品及其所需数量的列表。 props 和 state 被描述为 TypeScript 接口:
interface Props {
products: string[];
}
interface State {
quantities: { [key: string]: number };
}
class ShoppingBasket extends React.Component<Props, State> {
static defaultProps: Props = {
products: []
}
state: Readonly<State> = {
quantities: this.props.products.reduce((acc, product) => {
acc[product] = 1;
return acc;
}, {})
}
render() {
const { products } = this.props;
const { quantities } = this.state;
return (
<div>
<ul>
{products.map(product =>
<li>
<h2>{product}</h2>
<p>
Quantity:
<input
type="number"
value={quantities[product]}
/>
</p>
</li>
)}
</ul>
</div>
)
}
}
请注意,Props和State接口都是作为类类型的泛型参数指定的。两者都是可选的,默认情况下为空对象 ( {} )。通过指定类型,TypeScript 能够强类型化this.props和this.state。
下面这一行明确注释了组件的状态类属性类型:
state: Readonly<State> = {
通过明确指定state属性的类型,我们可以确保如果初始化的状态对象与State接口不匹配,编译器会抛出错误。使用Readonly<T>内置类型助手可确保如果您尝试直接修改this.state ,TypeScript 会抛出错误。
同样的明确性适用于ShoppingBasket.defaultProps静态类属性:
static defaultProps: Props = {
products: []
}
键入函数式组件的 props
我们并不会在ShoppingBasket组件中以内联方式呈现产品标题,而是通过将产品显示提取到其自己单独的组件中来演示如何静态地键入 React 功能组件。
功能组件可以使用const/let关键字或函数样式来输入:
const ProductDisplay = (props: { title: string }) => (
<h2>{props.title}</h2>
);
function ProductDisplay(props: { title: string }) {
return (
<h2>{props.title}</h2>
)
}
在这种情况下,我们不需要定义新的接口来描述ProductDisplay的 props,因为我们只传递产品标题。相反,我们使用带有title字符串属性的对象类型注释。您可以像在 TypeScript 中注释任何其他函数一样注释 React 函数组件的 props。
在函数组件中访问 Props Children
要访问功能组件中 React 提供的 prop children,你可以选择使用React.FunctionComponent<TProps>类型注释:
const ProductDisplay: React.FunctionComponent<{ title: string}> = (props) => (
<h2>{props.title} {props.children}</h2>
);
function ProductDisplay(props): React.FunctionComponent<{ title: string}> {
return (
<h2>{props.title} {props.children}</h2>
)
}
使用这种类型注释可以让 TypeScript 理解 React 组件的上下文,并使用 React 提供的默认 props (如children )来增强自定义 props 。
现在,明确注释props函数参数是多余的,因为我们将其作为函数返回类型中的TProps通用参数提供。
现在用新的ProductDisplay组件替换内联产品标题:
- <h2>{product}</h2>
+ <ProductDisplay title={product} />
如果对title属性使用了错误类型的值,TypeScript 现在将抛出编译器错误。
输入 React 事件处理程序
为了处理数量输入的值变化并更新ShoppingBasket状态,添加一个新的箭头函数类属性onQuantityChanged并通过输入的onChange prop 挂接到它:
class ShoppingBasket extends React.Component<Props, State> {
state: Readonly<State> = {
quantities: this.props.products.reduce((acc, product) => {
acc[product] = 1;
return acc;
}, {})
}
render() {
const { products } = this.props;
const { quantities } = this.state;
return (
<div>
<ul>
{products.map(product =>
<li>
<ProductDisplay title={product} />
<p>
Quantity:
<input
type="number"
value={quantities[product]}
+ onChange={this.onQuantityChanged(product)}
/>
</p>
</li>
)}
</ul>
</div>
)
}
+
+ onQuantityChanged = (product: string) =>
+ (e: React.ChangeEvent<HTMLInputElement>) => {
+ const quantity = parseInt(e.target.value, 10);
+ this.setState({
+ quantities: {
+ ...this.state.quantities,
+ [product]: quantity
+ }
+ });
+ }
}
通过使用箭头函数类属性,处理程序中的this关键字将绑定到类实例。这意味着您无需在类构造函数中添加bind调用。
onQuantityChanged函数是一个工厂函数,它将为每个给定产品返回一个 React 事件处理程序。React 事件参数通常使用*Event后缀输入,并采用表示目标元素的通用参数。
在上述情况下,onChange事件被添加到
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~