天天看点

TypeScript && React

环境搭建

我们当然可以先用脚手架搭建React项目,然后手动配置成支持TypeScript的环境,虽然比较麻烦,但可以让你更清楚整个过程。这里比较麻烦,就不演示了,直接用命令配置好。

1

npx create-react-app appname --typescript

可以安装一些自己需要的库及其声明文件,例如react-router-dom、axios、ant Design等。如果要安装ant design,还需要在开发环境库中安装一些依赖库,以帮助实现按需加载。

使用

有类型约束的函数组件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

import

React from 

"react"

;

import

{ Button } from 

"antd"

;

interface

Greeting {

name: string;

firstName?: string;

lastName?: string;

}

// 没有使用React.FC

const HelloOld = (props: Greeting) => <Button>你好{props.name}</Button>;

// 使用React.FC泛型类型

const Hello: React.FC<Greeting> = (props) => {

return

(

<Button>Hello {props.name}</Button>

)

};

export

{ Hello, HelloOld };

定义函数组件时,使用React.FC与不使用没有太多区别,没有为我们带来明显的好处,建议使用常规定义方式。

有类型约束的类组件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

import

React,{Fragment} from 

"react"

;

import

{ Button } from 

"antd"

;

interface

Greeting {

name: string;

firstName?: string;

lastName?: string;

}

interface

State {

count: number

}

// 泛型类型,第一个传入参数约束属性props,第二个约束状态state(内部数据)

class

HelloClass 

extends

React.Component<Greeting, State> {

state: State = {

count: 0

};

static

defaultProps = {  

// 属性默认值

firstName: 

""

,

lastName: 

""

,

};

render() {

return

(

<Fragment>

<p>点击了{

this

.state.count}次</p>

<Button onClick={()=>{

this

.setState({count: 

this

.state.count+1})}}>Hello{

this

.props.name}Class</Button>

</Fragment>

);

}

}

export

default

HelloClass;

有类型约束的高阶组件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

import

React from 

"react"

;

import

HelloClass from 

"./HelloClass"

;

interface

Loading {

loading: boolean;

}

function

HelloHoc<P>(params?: any) {

return

function

<P>(WrappedComponent: React.ComponentType<P>) { 

// P表示被包装组件的属性的类型

return

class

NewComponent 

extends

React.Component<P & Loading>{ 

// 这里使用交叉类型,为新组件增加一些属性,接口Loading定义了新增的属性声明

render(){

return

this

.props.loading ? <div>Loading</div> : <WrappedComponent {...

this

.props as P}/>

}

}

}

}

export

default

HelloHoc()(HelloClass);

高阶组件在ts中使用会有比较多的类型问题,解决这些问题通常不会很顺利,而且会存在一些已知的bug,这不是高阶组件本身的问题,而是React声明文件还没有很好地兼容高阶组件的类型检查,更好的方式是使用Hooks

有类型约束的Hooks

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

import

React, { useState, useEffect } from 

"react"

;

import

{ Button } from 

"antd"

;

interface

Greeting {

name: string;

firstName?: string;

lastName?: string;

}

const HelloHooks = (props: Greeting) => {

const [ count, setCount ] = useState(0); 

// 设了初值,所以不用定义类型

const [ text, setText ] = useState<string | 

null

>(

null

);

useEffect(()=>{

count > 5 && setText(

"休息一下"

);

},[count]); 

// 第二个参数的作用是,只有当count改变的时候,函数内的逻辑才会执行。

return

(

<>

<p>你点击了Hooks {count} 次 {text}</p>

<Button onClick={()=>{setCount(count+1)}}>{props.name}</Button>

</>

);

};

export

default

HelloHooks;

事件绑定

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

class

HelloClass 

extends

React.Component<Greeting, State> {

state: State = {

count: 0

};

clickHandle = (e: React.MouseEvent) => { 

// 事件对象e的类型使用内置的合成事件。在回调函数中,e的属性都会无效

e.persist(); 

// 将该事件从池中删除合成事件,可以正常使用

console.log(e);

// this.setState({count: this.state.count+1})

};

inputHandle = (e: React.FormEvent<HTMLInputElement>) => {

// e.persist();

console.log(e.currentTarget.value); 

// 此时编译器报错,认为没有value属性,需要指定<HTMLInputElement>泛型类型

// console.log(e.target.value); // 仍然不行

};

render() {

return

(

<Fragment>

<p>点击了{

this

.state.count}次</p>

<Button onClick={

this

.clickHandle}>Hello{

this

.props.name}Class</Button>

<input onChange={

this

.inputHandle}/>

</Fragment>

);

}

}

继续阅读