Effect Hook

Effect Hook 是什么

可以将 Effect Hook 看做是 class 组件中 componentDidMountcomponentDidUpdatecomponentWillUnmount 三个函数的组合。

Effect Hook 解决什么问题

Effect Hook 可以解决 class 组件同一生命周期函数经常包含不相关的逻辑,但又把相关逻辑分离到了不同生命周期的问题。Effect Hook 会在每次渲染后(挂载或更新,在 class 组件中,这两个过程是两个不同个生命周期)被执行,我们可以在 Effect Hook 中执行副作用操作,如数据获取、dom 更新、事件的监听和清除、定时器的设定和清理等,从而将相关逻辑聚集在同一个地方。

componentDidMountcomponentDidUpdate 不同,使用 useEffect 调度的 effect传给 useEffect 的函数叫做 effect) 不会阻塞浏览器更新屏幕,这让我们的应用看起来响应更快。

effect 的清除机制

当我们在 effect 中添加了监听、定时器等需要清理的资源时,可以给 effect 返回一个清理函数,将清理操作放到清理函数中。React 会在组件卸载、和下次执行对应 effect 时,执行此清理函数。

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

function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);

useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}

ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// 在 effect 函数中,返回一个清理函数,并将清理操作作为清理函数的内容:
// 清理函数会在下次执行此 effect 时,默认进行执行
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});

if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}

当 effect 内容不需要清理时,则无需返回函数。

1
2
3
4
useEffect(() => {
//effect 无需清理时 则不需要返回清理函数
document.title = `You clicked ${count} times`;
});

注意

逻辑分离

可以使用多个 effect,将不同的逻辑分离开。React 会按照 effect 声明的顺序依次调用组件中的每一个 effect。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function FriendStatusWithCounter(props) {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});

const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}

ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
// ...
}

控制渲染频率(性能)

可以通过给 useEffect 传递第二个参数(数组类型)来控制组件的渲染。

下面示例中,将 count 作为数组参数传递给 useEffect 时,react 只有当 count 的值发生变化时,才会调用 effect 函数和清理函数。

1
2
3
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新

当 useEffect 的第二个参数的数组中有多个元素时,只有当所有元素都没有发生变化时,react 才会忽略 effect的执行。

当 useEffect 的第二个参数的数组为空时,这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行。

语法校验

官方推荐启用 eslint-plugin-react-hooks 中的 exhaustive-deps 规则。此规则会在添加错误依赖时发出警告并给出修复建议