Skip to main content

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 对静态属性:displayNamepropTypesdefaultProps 提供了类型检查和自动补全;
  • React.FCchildren 提供了隐式的类型(ReactElement | null)

2、有状态组件

有状态组件存在 propsstate 属性,以类组件为例,使用 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>
}
}

上述通过泛型对 propsstate 进行类型定义,然后在使用时就可以在编译器中获得更好的智能提示。

所继承的 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;