写在前面

要想在react + typescript 项目中正确的使用 hooks ,请务必掌握泛型的使用,关于泛型的讲解,我在上一篇博文中详细的进行了介绍,如果你还不了解 typescript 中的泛型请看上一篇:正确认识typescript中的泛型

如何结合typescript正确使用hooks

在react中使用函数式组件,难免会使用到hooks,hooks赋予了函数式组件可以像 class 组件那样拥有状态的能力,那么如何结合typescript正确的使用hooks呢?

在react + typescript项目中正确使用useState

如果想编写一个 Header 组件,使其具备一个登录和退出的功能,登录的时候,显示一些提示已经登录的文字,退出的时候显示欢迎的文字,那么我们很容易知道:如果使用函数式组件来编写此 Header 组件,那么就一定会用到 useState ,因为登录与否的状态是该组件自身的状态,所以就会用到hooks中的 useState 来赋予函数式组件状态。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Header.tsx
import React, { useState } from 'react'

export default function Header() {
const [isLogin, setIsLogin] = useState(false);

const _logout = () => {
setIsLogin(false)
}

const _login = () => {
setIsLogin(true)
}

return (
<div>
{isLogin ? <p>You have logined in!</p> : <p>welcome, guest!</p>}
{isLogin ? <button onClick={_logout}>退出</button> : <button onClick={_login}>登录</button>}
</div>
)
}

其实在第五行const [isLogin, setIsLogin] = useState(false);使用useState赋予初始值false的时候,后续在调用setIsLogin函数时,ts 编译器就会自动推断你传入的参数是不是boolean类型的,若不是,则会报错。第五行,我们也可以进行显式的进行限制,传入泛型,就像这样const [isLogin, setIsLogin] = useState<boolean>(false);

我们也可以把要显示的文案也抽象成为组件的一个状态,这样我们就不需要再写三元表达式了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import React, { useState } from 'react'

export default function Header() {
const [isLogin, setIsLogin] = useState<boolean>(false);
const [text, setText] = useState<string>('');

const _logout = () => {
setIsLogin(false);
setText('welcome, guest!');
}

const _login = () => {
setIsLogin(true);
setText('You have logined in!');
}

return (
<div>
<p>{ text }</p>
{isLogin ? <button onClick={_logout}>退出</button> : <button onClick={_login}>登录</button>}
</div>
)
}

这里的函数setIsLogin只能传入布尔类型的值,函数setText只能传入 string 类型的值,传别的类型都会报错。
这里的两个状态比较简单,一个是布尔类型,一个是 string 类型,可能体现不出typescript的强大之处,那么我们再增加一个状态用户信息:const [user, setUser] = useState<UserType | null>(null),完整代码如下:

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
34
35
36
37
38
import React, { useState } from 'react'

type UserType = {
name: string;
age: number;
addr: string;
}

export default function Header() {
const [isLogin, setIsLogin] = useState<boolean>(false);
const [text, setText] = useState<string>('');
const [user, setUser] = useState<UserType | null>(null)

const _logout = () => {
setIsLogin(false);
setText('welcome, guest!');
setUser(null)
}

const _login = () => {
setIsLogin(true);
setText('You have logined in!');
setUser({
name: '小明',
age: 18,
addr: '深圳市南山区深圳湾河里'
})
}

return (
<div>
<p>{ text }</p>
<p>username is {user?.name}, user's age is {user?.age}, user's address is {user?.addr}</p>
{isLogin ? <button onClick={_logout}>退出</button> : <button onClick={_login}>登录</button>}
</div>
)
}

我们给useState显式的传入泛型UserType或者null,就表示我们在后续调用setUser的时候,就只能传入UserType 类型的值或者 null 类型的值,否则ts编译器就会告诉你传值错误。这样做,我们在开发过程中可以省去了很多麻烦,可维护性也更好。

代码地址及文件路径

注:
本文代码github仓库地址:ts中正确使用useState
文件路径:src/components/Header.tsx