使用Effect hook可以在函数组件中执行副作用
副作用 - 指一段和当前执行结果无关的代码。
- data fetching
- setting up a subscription
- manually changing the DOM
- logging
useEffect
可以看作是 componentDidMount
, componentDidUpdate
和 componentWillUnmount
的结合,且useEffect
不会阻塞浏览器更新screen。
useEffect 使用方法及执行时机:
-
Effects Without Cleanup - 该例子会在每次
render
之后都执行effect
- 组建挂载后-
componentDidMount
- 每次
update
后-componentDidUpdate
- 组建挂载后-
useEffect(() => {
document.title = `You clicked ${count} times`;
})
-
Effects With Cleanup - 该例子的
effect
返回了一个函数- 其他内容在每次
render
之后会执行 - 返回的函数内容会在组件卸载之前(等同于
componentWillUnmount
)执行
- 其他内容在每次
useEffect(() => {
const handStatusUpdated = (status) => {
setIsOnline(status.isOnline)
}
ChatAPI.subscribe(friend.id, handStatusUpdated);
return ChatAPI.unsubscribe(friend.id, handStatusUpdated);
})
- 在同一个组件中多次使用
useEffect
- React会按照指定的顺序应用每个effect -
Skipping Effects - 只有
count
改变才会re-run
该effect
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count])
useEffect(() => {
const handStatusUpdated = (status) => {
setIsOnline(status.isOnline)
}
ChatAPI.subscribe(friend.id, handStatusUpdated);
return ChatAPI.unsubscribe(friend.id, handStatusUpdated);
}, [friend.id])
- 只在
mount
和unmount
时运行的effect
- 依赖项传入空数组并返回函数
useEffect(() => {
// code block mount时执行
return () => {
// code block unmount时执行
}
}, [])
Hooks 的依赖项
Hooks 提供了让你监听某个数据变化的能力。这个变化可能会触发组件的刷新,也可能是去创建一个副作用,又或者是刷新一个缓存。那么定义要监听哪些数据变化的机制,其实就是指定 Hooks 的依赖项。
定义依赖项时,需要注意以下三点:
- 依赖项中定义的变量一定是会在回调函数中用到的,否则声明依赖项其实是没有意义的。
- 依赖项一般是一个常量数组,而不是一个变量。因为一般在创建
callback
的时候,你其实非常清楚其中要用到哪些依赖项了。 -
React
会使用浅比较来对比依赖项是否发生了变化,所以要特别注意数组或者对象类型。
function Sample() {
// 这里在每次组件执行时创建了一个新数组
const todos = [{ text: 'Learn hooks.'}];
useEffect(() => {
console.log('Todos changed.');
}, [todos]);
}
代码的原意可能是在 todos
变化的时候去产生一些副作用,但是这里的 todos
变量是在函数内创建的,实际上每次都产生了一个新数组。
useEffect 与三种生命周期方法
useEffect(() => {
// componentDidMount + componentDidUpdate
console.log('这里基本等价于 componentDidMount + componentDidUpdate');
return () => {
// componentWillUnmount
console.log('这里基本等价于 componentWillUnmount');
}
}, [deps])
基本等价于的含义:
- 一方面,
useEffect
(callback) 这个Hook
接收的callback
,只有在依赖项变化时才被执行。而传统的componentDidUpdate
则一定会执行。在componentDidUpdate
中,我们通常都需要手动判断某个状态是否发生变化,然后再执行特定的逻辑。 - 另一方面,
callback
返回的函数(一般用于清理工作)在下一次依赖项发生变化以及组件销毁之前执行,而传统的componentWillUnmount
只在组件销毁时才会执行。
useEffect 接收的返回值是一个回调函数,这个回调函数不只是会在组件销毁时执行,而且是每次 Effect 重新执行之前都会执行,用于清理上一次 Effect 的执行结果。
总结
学习useEffect
要理解什么是副作用以及使用useEffect
传入不同的参数时能达到什么效果。虽然 Hooks
在功能上基本可以映射到传统的 Class
组件的生命周期方法,但是它们却又不是完全等价的。在实现具体的业务功能的时候,都应该尽量从 Hooks
的语义角度出发去思考组件是如何展现和交互的,这样才能更加顺滑地切换到函数组件的开发方式。
在函数组件中你要思考的方式永远是:当某个状态发生变化时,我要做什么,而不再是在 Class 组件中的某个生命周期方法中我要做什么。