useEffect 是为了让函数组件有和类组件一样的生命周期函数,可以在函数组件内通过useEffect实现接口调用、数据加载等操作。
useEffect(callback[,[]])
参数:
第一个参数是要执行的函数
第二个参数是一个依赖项数组数组(根据需求第二个参数可选是否填写),根据数组里的变量是否变化决定是否执行函数
在useEffect内部需要的值,可以通过第二个参数传入,可传递多个,用于请求数据或者加载页面
用法
useEffect:在函数组件中,使用生命周期函数
useEffect(callback):没设置依赖
- 第一次渲染完毕后,执行callback,等价于 componentDidMount
- 在组件每一次更新完毕后,也会执行callback,等价于 componentDidUpdate
useEffect(callback,[]):设置了,但是无依赖
- 只有第一次渲染完毕后,才会执行callback,每一次视图更新完毕后,callback不再执行
- 类似于 componentDidMount
useEffect(callback,[依赖的状态(多个状态)]):
- 第一次渲染完毕会执行callback
- 当依赖的状态值(或者多个依赖状态中的一个)发生改变,也会触发callback执行
- 但是依赖的状态如果没有变化,在组件更新的时候,callback是不会执行的
函数写法
useEffect(()=>{
return ()=>{
// 返回的小函数,会在组件释放的时候执行
// 如果组件更新,会把上一次返回的小函数执行「可以“理解为”上一次渲染的组件释放了」
};
});
函数操作,是先添加到effect链表,然后在第二次渲染时运行内部操作
useEffect原理
useEffect运行,是在当前视图更新完毕【产生真实DOM】之后,通知callback去执行effect链表
有一个effect链表,在组件渲染的时候,方法执行时,执行组件内部的useEffect 的callback,通过mountEffect的方法将useEffect 的callback放到effect链表当中
等到组件更新完,会通过updateEffect方法通知链表中指定的callback执行的过程
注意点⚠️
1.useEffect必须在函数的最外层上下文中调用,不能把其嵌入到条件判断、循环等操作语句中
2.异步获取数据(使用promise / async await 等用法)
例子:
import React, { useState, useEffect } from "react";
import { Button } from 'antd';
// 模拟从服务器异步获取数据
const queryData = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve([10, 20, 30]);
}, 1000);
});
};
const Demo = function Demo() {
let [num, setNum] = useState(0);
/* // useEffect必须在函数的最外层上下文中调用,不能把其嵌入到条件判断、循环等操作语句中
if (num > 5) {
useEffect(() => {
console.log('OK');
});
} */
useEffect(() => {
if (num > 5) {
console.log('OK');
}
}, [num]);
// 第一次渲染完毕后,从服务器异步获取数据
/* // useEffect如果设置返回值,则返回值必须是一个函数「代表组件销毁时触发」;下面案例中,callback经过async的修饰,返回的是一个promise实例,不符合要求!!
useEffect(async () => {
let data = await queryData();
console.log('成功:', data);
}, []); */
/* useEffect(() => {
queryData()
.then(data => {
console.log('成功:', data);
});
}, []); */
useEffect(() => {
const next = async () => {
let data = await queryData();
console.log('成功:', data);
};
next();
}, []);
const handle = () => {
setNum(num + 1);
};
return <div className="demo">
<span className="num">{num}</span>
<Button type="primary"
size="small"
onClick={handle}>
新增
</Button>
</div>;
};
export default Demo;
useLayoutEffect 与 # useEffect 二者区别
二者用法一致 但渲染时机不同
useLayoutEffect的渲染位置在虚拟节点渲染 中的 render方法(需要将DOM对象创建出来)之后,然后还没有渲染节点,在浏览器绘制和渲染之前
useEffect是在浏览器绘制和渲染之后
渲染时机的不同,为了防止阻塞渲染 尽可能使用 useEffect
useLayoutEffect会阻塞浏览器渲染真实DOM,优先执行Effect链表中的callback;
useEffect不会阻塞浏览器渲染真实DOM,在渲染真实DOM的同时,去执行Effect链表中的callback;
- useLayoutEffect设置的callback要优先于useEffect去执行!!
- 在两者设置的callback中,依然可以获取DOM元素「原因:真实DOM对象已经创建了,区别只是浏览器是否渲染」
- 如果在callback函数中又修改了状态值「视图又要更新」
- useEffect:浏览器肯定是把第一次的真实已经绘制了,再去渲染第二次真实DOM
- useLayoutEffect:浏览器是把两次真实DOM的渲染,合并在一起渲染的
如果链表中的callback执行又修改了状态值「视圈更新」 对于 useEffect来讲:第一次真实DOM已经渲染,组件更新 会重新渲染真实的DOM; 所以频繁切换的时候,会出现样式/内容闪烁!!
对于 useLayoutEffect 来讲:第一次真实DOM还未渲染,遇到callback中修改了状态,视图立即更新,创建出新的virtualDOM,然后和上一次的virtuaIDOM合并在一起渲染为真实DOM;也就是 此类需求下,真实DOM只渲染一次,不会出现内容/样式的闪烁,