"小和山的菜鸟们",为前端开发者提供技术相关资讯以及系列基础文章。为更好的用户体验,请您移至咱们官网小和山的菜鸟们 ( xhs-rookies.com/ ) 进行学习,及时获取最新文章。前端
"Code tailor" ,若是您对咱们文章感兴趣、或是想提一些建议,微信关注 “小和山的菜鸟们” 公众号,与咱们取的联系,您也能够在微信上观看咱们的文章。每个建议或是赞同都是对咱们极大的鼓励!react
这篇文章,咱们主要目的是了解一下 共享状态钩子(useEffect) 的使用.web
Effect Hook
可让你在函数组件中执行反作用操做,换句话说能够完成一些相似 Class 中生命周期的功能。数组
useEffect()
的使用以下面语句。微信
useEffect(() => {
dosomeing(); // 执行一些反作用操做
},[]);
复制代码
useEffect 须要参数markdown
第一个参数:该 Hook
接收一个包含命令式、且可能有反作用代码的函数。函数
第二个参数:该 useEffect
在哪些 state
发生变化时,才从新执行;oop
useEffect 作了什么学习
经过 useEffect
的 Hook
,能够告诉 React
须要在渲染后执行某些操做;而 useEffect
的第一个参数要求咱们传递一个函数,在 React
执行完更新 DOM
操做以后,就会执行这个函数,默认状况下,不管是组件第一次渲染以后,仍是组件更新以后,都会执行这个函数,咱们能够在这个要传递的函数中执行一些相似 Class
组件中生命周期要作的工做,好比:改变 DOM、添加订阅、设置定时器、记录日志等。 要知道,在没有这个 useEffect Hook
以前在函数组件内的这些反作用操做是不被容许的。优化
提示
若是你熟悉
React class
的生命周期函数,你能够把useEffect
Hook 看作componentDidMount
,componentDidUpdate
和componentWillUnmount
这三个函数的组合。
在 Class
组件中,某些反作用代码,咱们须要在 componentWillUnmount
中清除,好比咱们的建立计时器与清除计时器,一样的,在函数组件卸载时也须要清除 effect
建立的诸如订阅或计时器 ID 等资源。要实现这一点,useEffect
函数需返回一个清除函数。如下就是一个建立计时器的例子:
const [count, addCount] = useState(0)
const [timer, addTimer] = useState(1000)
useEffect(() => {
const interval = setInterval(() => {
// 建立计时器
addCount((val) => val + 1)
}, timer)
return () => {
// 返回清除函数
clearInterval(interval) // 清除计时器
}
})
复制代码
为防止内存泄漏,清除函数会在组件卸载前执行。另外,若是组件屡次渲染(一般如此),则在执行下一个 effect
以前,上一个 effect
就已被清除。在上述示例中,意味着组件的每一次更新都会建立新的计时器。若想避免每次更新都触发 effect
的执行,这就须要用到 useEffect
的第二个参数。
默认状况下,effect
会在每轮组件渲染完成后执行。这样的话,一旦 effect
的依赖发生变化,它就会被从新建立。
然而,在某些场景下这么作可能会矫枉过正。好比,上面计时器的例子中,咱们并不须要在每次组件更新时都建立新的计时器,而是仅须要在 source prop
改变时从新建立。
这里就要用到 useEffect
接受的第二个参数,它是 effect
所依赖的值数组。更新后的示例以下:
const [count, addCount] = useState(0)
const [timer, addTimer] = useState(1000)
useEffect(() => {
const interval = setInterval(() => {
// 建立定时器
addCount((val) => val + 1)
}, timer)
return () => {
clearInterval(interval) // 清除定时器
}
}, [count])
复制代码
当传入第二参数的时候,在这里也就是当 count
改变后才会从新建立计时器,也就是说,useEffect
会根据咱们传入的第二参数,来决定本次更新是否执行。
注意
- 若是你要使用此优化方式,请确保数组中包含了全部外部做用域中会发生变化且在 effect 中使用的变量,不然你的代码会引用到先前渲染中的旧变量。
- 若是想执行只运行一次的 effect(仅在组件挂载和卸载时执行),能够传递一个空数组(
[]
)做为第二个参数。
相信你如今已经对 useEffect
有所了解了,那咱们用一个简单的例子,来感觉一下在函数组件中使用 Hook
和 Class
组件的区别。
有一个简单的计时器,默认以 1000ms
的时间间隔计数,点击加按钮后,计时时间间隔增长,计时器计数变慢。
首先咱们要在类组件中声明一个变量 count
来计数,一个变量 timer
来控制计数时间间隔,每隔 timer
个时间, count
加一,而后添加一个按钮,每次点击时, timer
增长 1000ms
。同时 count
增长速度变慢。
class Example extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 0,
timer: 1000,
}
}
componentDidMount() {
this.interval = setInterval(() => {
// 建立定时器
this.setState({
count: this.state.count + 1,
})
}, this.state.timer)
}
componentDidUpdate() {
clearInterval(this.interval) // 先清除上一个定时器
this.interval = setInterval(() => {
// 建立以 更新后的timer 计时间隔定时器
this.setState({
count: this.state.count + 1,
})
}, this.state.timer)
}
componentWillUnmount() {
clearInterval(this.interval) // 卸载时清除定时器
}
render() {
return (
<div> <p>当前count值为:{this.state.count}</p> <p>当前计数时间间隔为:{this.state.timer}ms</p> <button onClick={() => this.setState({ timer: this.state.timer + 1000 })}>Click me</button> </div>
)
}
}
复制代码
咱们能够发现,在这个 class 中,咱们须要在两个生命周期函数中编写重复的代码。还要在组件卸载时清除计时器,大量的重复代码,显得很是繁琐,那么在函数组件中使用 Effect Hook
会是怎样的呢?
import React, { useState, useEffect } from 'react'
function Example() {
const [count, addCount] = useState(0)
const [timer, addTimer] = useState(1000)
useEffect(() => {
const interval = setInterval(() => {
addCount((val) => val + 1)
}, timer)
return () => {
clearInterval(interval)
}
}, [count])
return (
<div> <p>当前count的值为:{count}</p> <p>当前计数时间间隔为: {timer}ms</p> <button onClick={() => addTimer((val) => val + 1000)}>Click me</button> </div>
)
}
复制代码
看了这个例子以后,你是否是很惊奇的发现,对比 Class
组件的 constructor
函数没有了,写在三个生命周期中的函数在函数组件被 useEffect
一个家伙解决了!是否是看起来很精简。相信你有下面几个问题:
useEffect
作了什么?
经过使用这个 Hook
,你能够告诉 React
组件须要在渲染后执行某些操做。React
会保存你传递的函数(咱们将它称之为 “effect”),而且在执行 DOM
更新以后调用它。
为何在组件内部调用 useEffect
?
将 useEffect
放在组件内部让咱们能够在 effect
中直接访问 count state
变量(或其余 props
)。
useEffect
会在每次渲染后都执行吗?
是的,默认状况下,它在第一次渲染以后和每次更新以后都会执行(你能够利用前面提到的第二个参数来控制渲染条件)。并且,React
保证了每次运行 effect
的同时,DOM
都已经更新完毕。
useEffect Hook
实际上为咱们带来了,在函数组件中也能处理反作用的功能,他比如componentDidMount
,componentDidUpdate
和 componentWillUnmount
这三个函数的组合。
useEffect Hook
与 Class
函数的生命周期不一样的一点:useEffect
在 DOM 都已经更新完毕才执行,componentDidMount
则是在组件挂在后(插入 DOM 树中)当即调用,componentDidUpdate()
会在更新后会被当即调用。首次渲染不会执行此方法。componentWillUnmount()
会在组件卸载及销毁以前直接调用。
useEffect Hook
的出现以前,组件开发中逐渐会被状态逻辑和反作用充斥。每一个生命周期经常包含一些不相关的逻辑。而它的出现帮助咱们更好的拆分这些状态逻辑,并不是强制按照生命周期划分,为开发增添了许多的可能。
在下节中,咱们将为你们介绍 useRefs
,敬请期待!