Effect Hook
Effect Hook:用于在函数组件中处理副作用,类似我们类组件中的生命周期函数的,ComponentDidMount(组件刚刚挂载需要进行数据请求等副作用),ComponentDIdUpdate(组件数据已经更新并且页面已经完成挂载的对dom操作的副作用),ComponentwillUnMount(组件即将卸载销毁清楚定时器等副作用),类组件生命周期详情
语法
// 第一个请求参数是一个回调函数,用于操作副作用的, // 第二个参数是一个数组,用于标记回调函数里面的依赖关系,react 会根据数组里面的值来进行依赖收集,从而来判断回调函数是否执行 useEffect(() => {}, [])
该hooks 也和state hooks 一样,可以在同一函数里面书写多个,实现代码逻辑的分离。便于代码的维护以及代码的可读性。 stateHooks 详见
副作用:
1.ajax请求
2.计时器
3.其他异步操作
4.更改真实DOM对象
5.本地存储
6.其他会对外部产生影响的操作
函数:useEffect,该函数接收一个函数作为参数,接收的函数就是需要进行副作用操作的函数
细节
1. 副作用函数的运行时间点
是在页面完成真实的UI渲染之后。因此它的执行是异步的
1.与类组件中componentDidMount和componentDidUpdate的区别
2.componentDidMount和componentDidUpdate,更改了真实DOM,但是用户还没有看到UI更新,同步的。
3.useEffect中的副作用函数,更改了真实DOM,并且用户已经看到了UI更新,异步的。
案例
import React, { useEffect, useState } from 'react' export default function TestEffectHook() { // 计数器 const [count, setCount] = useState(0); // 改变网页的title useEffect(() => { document.title = `title ${count}`; // 为了证明effect是异步的,我在这里直接报错,看看ui是否会先出现,然后在报错,并且网页的title有没有改变 throw new Error('我要报错') }) console.log('我会比effect优先输出') return ( <div> <p>{count}</p> <button onClick={() => { setCount(pre => pre + 1) }}> + 1 </button> </div> ) }
效果
在效果图中,ui是会先出现的,然后我们的title值才会从 viteApp 改成 title 0,所以可以证明 effect的执行时间点是在等ui 渲染之后再来进行执行的,并且打印我会优先effect会优先输出。也就是异步执行
2. 每个函数组件中,可以多次使用useEffect,但不要放入判断或循环等代码块中。
对于这个概念,在所有的hooks 中都是同样的,可以使用多个useEffect,但是不能在判断或者循环的代码块中使用,原因是和hooks的实现原理有关,所有的hooks 会使用一个类似地图的map来及记录每一个hooks的顺序,如果顺序改变,会引起未知的错误。
详情详见
3. useEffect中的副作用函数,可以有返回值,返回值必须是一个函数,该函数叫做清理函数
1.该函数运行时间点,在每次运行副作用函数之前
2.首次渲染组件不会运行
3.组件被销毁时一定会运行
案例
import React, { useEffect, useState } from 'react' export default function TestEffectHook() { // 计数器 const [count, setCount] = useState(0); // 改变网页的title useEffect(() => { console.log('effect副作用函数执行'); return () => { console.log('cleanup 函数执行了'); } }); // 控制组件显示隐藏 const [canShow, setCanShow] = useState(true) return ( <div> {canShow && ( <div> <p>{count}</p> <button onClick={() => { setCount(pre => pre + 1) }}> + 1 </button> </div> )} <button onClick={() => { setCanShow(!canShow) }}>显示/隐藏</button> </div> ) }
效果
一切看效果,默认清除副作用函数不执行,只有在组件销毁,或者数据更新的时候,才会优先执行清理副作用函数,然后在执行副作用函数
4. useEffect函数,可以传递第二个参数
1.第二个参数是一个数组
2.数组中记录该副作用的依赖数据
3.当组件重新渲染后,只有依赖数据与上一次不一样的时,才会执行副作用
4.所以,当传递了依赖数据之后,如果数据没有发生变化
1.副作用函数仅在第一次渲染后运行
2.清除副作用只会在组件销毁的时候执行
案例
import React, { useEffect, useState } from 'react' export default function TestEffectHook() { // 计数器 const [count, setCount] = useState(0); // 改变网页的title useEffect(() => { console.log('effect副作用函数执行'); document.title = `title ${count}` return () => { console.log('cleanup 函数执行了'); } },[count]); return ( <div> <div> <p>{count}</p> <button onClick={() => { setCount(pre => 10) }}> 只改变一次title </button> </div> </div> ) }
效果
5. 副作用函数中,如果使用了函数上下文中的变量,则由于闭包的影响,会导致副作用函数中变量不会实时变化。
每一个effect 函数是单独指向当前依赖值的,并且执行的方式是放在执行栈中,先进先出,然后依次执行。
案例
import React, { useEffect, useState } from 'react' export default function TestEffectHook() { // 计数器 const [count, setCount] = useState(0); // 改变网页的title useEffect(() => { console.log('effect副作用函数执行'); document.title = `title ${count}` // 每一秒后打印结果 setTimeout(() => { console.log(count); },2000); return () => { console.log('cleanup 函数执行了'); } },[count]); return ( <div> <div> <p>{count}</p> <button onClick={() => { setCount(pre => pre + 1) }}> + 1 </button> </div> </div> ) }
效果
从结果中,我们可以看到,我们要打印的count还没有2s就打印了,有兴趣的同学可以拿代码设置更长的时间来进行打印,会发现七九
6. 副作用函数在每次注册时,会覆盖掉之前的副作用函数。
因此,尽量保持副作用函数稳定,否则控制起来会比较复杂。这一句话啥意思呢,我们的effect里面可以接收一个函数,不到万不得已,不建议使用动态的函数哦