Hook
是 React 16.8
的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性,Hook 不会影响你对React概念得理解。
恰恰相反,Hook 为已知的 React 概念提供了更直接的 API:props, state,context,refs 以及生命周期。稍后我们将看到,Hook 还提供了一种更强大的方式来组合他们。从而使得函数式组件从无状态的变化为有状态的。
React 的类型包 @types/react 中也同步把 React.SFC (Stateless Functional Component) 改为了 React.FC (Functional Component)。
通过这一升级,原先 class 写法的组件也就完全可以被函数式组件替代。虽然是否要把老项目中所有类组件全部改为函数式组件因人而异,但新写的组件还是值得尝试的,因为代码量的确减少了很多,尤其是重复的代码(例如 componentDidMount + componentDidUpdate + componentWillUnmount = useEffect)。
以下是下面会讲到得三个主要问题:
- 如何在组件加载时发起异步任务
- 如何在组件交互时发起异步任务
- 其他陷阱
TL;DR
- 使用
useEffect
发起异步任务,第二个参数使用空数组可实现组件加载时执行方法体,返回值函数在组件卸载时执行一次,用来清理一些东西,例如计时器。 - 使用 AbortController 或者某些库自带的信号量 (
axios.CancelToken
) 来控制中止请求,更加优雅地退出。 - 当需要在其他地方(例如点击处理函数中)设定计时器,在
useEffect
返回值中清理时,使用局部变量或者useRef
来记录这个timer
。不要使用useState
。 - 组件中出现
setTimeout
等闭包时,尽量在闭包内部引用 ref 而不是 state,否则容易出现读取到旧值的情况。 useState
返回的更新状态方法是异步的,要在下次重绘才能获取新值。不要试图在更改状态之后立马获取状态。
如何在组件加载时发起异步任务
这类需求非常常见,典型的例子是在列表组件加载时发送请求到后端,获取列表后展现。
发送请求也属于 React 定义的副作用之一,因此应当使用 useEffect
来编写。基本语法我就不再过多说明,代码如下:
import React, { useState, useEffect } from 'react';
const SOME_API = '/api/get/value';
export const MyComponent: React.FC<{}> = () => {
const [loading, setLoad