使用 TypeScript 编写 React 组件
介绍
在本指南中,您将学习如何使用编译为 JavaScript 的语言TypeScript编写React.js组件。使用 TypeScript 和 React 允许您开发具有可识别 props 和 state 对象的强类型组件,这将确保您的组件的任何用法都经过编译器的类型检查。
使用 React 和 TypeScript 组合组件会出现一些乍一看可能不直观的细微差别,本指南将涵盖最常见的场景。
本指南假设您已经熟悉 React 和 TypeScript 作为独立的技术,以及如何使用 TypeScript 静态类型化 React 组件。
引用 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 的跳棋游戏的组合组件。
跳棋由棋子组成,棋子在 10x10 的方格网格上移动,并按照特定规则移动
组合优于继承
传统上,游戏在本质上更面向对象。在跳棋游戏中,圆形棋子(“棋子”)只能向前移动。一旦它们到达“王行”,它们就可以被加冕为“王”,并获得向后移动等额外优势,但它们仍然继承了与棋子相同的行为。
在为这两种“类型”的游戏组件建模 React 组件时,可能会倾向于使用继承以相同的方式对其进行建模:
interface PieceProps {
color: "red" | "black";
}
const Piece = (props: PieceProps) => (
<svg />
)
class Pawn extends React.Component<PawnProps> {
render() {
return <Piece color={this.props.color} />;
}
/* pawn behaviors */
}
class King extends Pawn {
render() {
return (
<React.Fragment>
<Piece color={this.props.color} />
<Piece color={this.props.color} />
</React.Fragment>
);
}
/* specific King behaviors */
}
King组件需要显示两个棋子堆叠在一起,因此它重写了 Pawn 组件的渲染方法。这种继承模式将允许King组件重用其Pawn基类中的行为。
在 React 中,我们不鼓励这样做,而是建议将 React 组件组合在一起。通过使用继承,它会强制组件的所有变体都是基于类的组件,而不是允许无状态函数组件。
随着 TypeScript 的加入,组件 props 和状态的继承机制变得更加困难。例如,如果King需要与Pawn不同的 props 怎么办?要使用 props 对其进行强类型化,Pawn需要成为通用类组件:
class Pawn<TProps = PawnProps> extends React.Component<TProps> {}
class King extends Pawn<KingProps> {}
为了便于说明,道具是什么并不重要,只是静态输入和自定义继承组件的渲染变得很麻烦。即使在上面的例子中,我也使用默认的泛型参数(TProps = PawnProps )将Pawn的默认道具类型设置为PawnProps,否则您在使用组件时将被迫始终指定道具类型:
<Pawn<PawnProps> color="red" />
相反,让我们介绍一下如何组合React 组件的几种模式,这些模式可以实现与继承相同的目标,但是以更可控、更灵活、更安全的方式。
专业化组合
我们不妨换一种思路,而不是让King继承自Pawn。King是一种特殊的Pawn ,由两个棋子堆叠而成。
我们可以使用<Stack />组件来封装多个棋子的渲染,并且每个棋子都可以自定义自己的Stack组件:
interface PieceProps {
color: "red" | "black";
}
interface StackProps {
size: number;
pieceProps: PieceProps;
}
const Stack = ({ size, pieceProps }: StackProps) => (
<React.Fragment>
{new Array(size).map((_, index) => (
<Piece key={index.toString()} {...pieceProps} />
))}
</React.Fragment>
);
const Pawn = (props: PieceProps) => <Stack size={1} pieceProps={props} />;
const King = (props: PieceProps) => <Stack size={2} pieceProps={props} />;
现在King和Pawn是专门的组件,它们以特定方式配置Stack组件的 props。提供pieceProps prop 是为了允许您将 props 传递给<Piece />组件。
在 TypeScript 中,我们定义了两个代表 prop 类型的接口,并在Pawn和King组件之间共享相同的PieceProps。
<Stack />组件接受pieceProps属性,然后将其向下传播到<Piece />组件上。这有时称为“属性传播”或“属性传递”,让消费者在不了解Stack工作原理的情况下更好地控制行为。
容器和子项
要将棋子放置在棋盘上,很可能有一个包含方格网格的Board组件。
由于Square可以包含任何类型的棋子,我们可以利用 React 的children prop,它代表一种传递子 React 元素的方法。
请记住,尽管在 React 中使用 JSX 最为常见,但您也可以使用React.createElement以纯 JS 形式编写 React 组件,其第三个参数是...children,可以实现组件嵌套:
React.createElement("div", null,
React.createElement("p", null, "Some text"),
React.createElement("p", null, "Another paragraph"));
JSX 中的等效代码如下:
<div>
<p>Some text</p>
<p>Another paragraph</p>
</div>
<p />元素是<div />元素的子元素。在 React 中,任何子元素都可以从父组件的 props 中作为children访问:
interface SquareProps {
color: "red" | "black";
}
const Square: React.FunctionComponent<SquareProps> = props => (
<div style={{ backgroundColor: props.color }}>{props.children}</div>
);
使用React.FunctionComponent类型注释,我们可以对<font style="vertical-align: inherit
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~