TS 在 React 中的应用
一、安装依赖
使用 TypeScript 编写 React 代码,除了需要 Typescript 库之外,还需要安装 @types/react、@types/react-dom
yarn add @types/react
yarn add @types/react-dom
二、使用方式
1、无状态组件
主要用于展示 UI,用 JS 声明如下:
import * as React from 'react'
export const Logo = (props) => {
const { logo, className, alt } = props
return <img src={logo} className={className} alt={alt} />
}
如果是 TS 文件会出现报错提示,原因在于没有定义 porps 的类型,可以使用 interface 接口来定义 porps:
import * as React from 'react'
interface LogoProps {
logo?: string
className?: string
alt?: string
}
export const Logo = (props: LogoProps) => {
const { logo, className, alt } = props
return <img src={logo} className={className} alt={alt} />
}
但 props 中存在 children
属性,需要在每个 props 接口中多定义一个 children
:
interface LogoProps {
logo?: string
className?: string
alt?: string
children?: React.ReactNode
}
更加规范的写法是使用 React 中定义好的 FC
属性,里面已经定义好 children
类型:
export const Logo: React.FC<LogoProps> = (props) => {
const { logo, className, alt } = props
return <img src={logo} className={className} alt={alt} />
}
- React.FC 显式地定义了返回类型,其他方式是隐式推导的;
- React.FC 对静态属性:
displayName
、propTypes
、defaultProps
提供了类型检查和自动补全; - React.FC 为
children
提供了隐式的类型(ReactElement | null)
2、有状态组件
有状态组件存在 props 和 state 属性,以类组件为例,使用 TS 声明如下:
import * as React from 'react'
interface AppProps {
color: string
size?: string
}
interface AppState {
count: number
}
class App extends React.Component<AppProps, AppState> {
public state = {
count: 1,
}
public render() {
return <div>Hello world</div>
}
}
上述通过泛型对 props、state 进行类型定义,然后在使用时就可以在编译器中获得更好的智能提示。
所继承的 Component 泛型类的定义文件 node_modules/@types/react/index.d.ts:
class Component<P, S> {
readonly props: Readonly<{ children?: ReactNode }> & Readonly<P>
state: Readonly<S>
}
可以看到,state 属性定义了可读类型,目的是为了防止直接调用 this.state 更新状态。
3、受控组件
受控组件的特性在于元素的内容通过组件的状态 state 进行控制,组件内部的事件是合成事件,不等同于原生事件,举个例子:
一个 input 组件修改内部的状态,常见的定义如下:
private updateValue(e: React.ChangeEvent<HTMLInputElement>) {
this.setState({ itemText: e.target.value })
}
常用 Event 事件对象类型:
- ClipboardEvent
<T = Element>
剪贴板事件对象 - DragEvent
<T = Element>
拖拽事件对象 - ChangeEvent
<T = Element>
Change 事件对象 - KeyboardEvent
<T = Element>
键盘事件对象 - MouseEvent
<T = Element>
鼠标事件对象 - TouchEvent
<T = Element>
触摸事件对象 - WheelEvent
<T = Element>
滚轮事件对象 - AnimationEvent
<T = Element>
动画事件对象 - TransitionEvent
<T = Element>
过渡事件对象
T 接收一个 DOM 元素类型
三、示例
下面以一个 Button 组件的 TS 声明为例:
import React from 'react';
import classNames from 'classnames';
import './index.scss';
export interface ButtonProps {
/**
* 类名
*/
className?: string;
/**
* 是否禁用按钮
* @default false
*/
disabled?: boolean;
/**
* 按钮尺寸
* @default medium
*/
size?: 'small' | 'medium' | 'large';
/**
* 自定义样式
*/
style?: React.CSSProperties;
/**
* 按钮类型,用于描述组件不同的应用场景
* @default primary
*/
type?: 'info' | 'primary' | 'danger' | 'warning' | 'success';
}
/**
* 按钮组件
*/
const Button: React.FC<ButtonProps> = (props) => {
const {
children = '',
className,
disabled = false,
size = 'medium',
style,
type = 'primary',
...buttonProps
} = props;
return (
<button
className={classNames(
'i-button',
`i-button--type-${type}`,
`i-button--size-${size}`,
disabled && 'i-button-disabled',
className,
)}
style={{ ...style }}
disabled={disabled}
{...buttonProps}
>
{children}
</button>
);
};
Button.displayName = 'Button';
export default Button;