useEffect
- 语法:useEffect(callback,[])有四种使用方式
-
useEffect(callback)
- 没有设置依赖 第一次渲染完毕后,执行callback,等价于 componentDidMount
- 组件每一次更新 都会调用 相当于class组件里面的componentDidUpdate
import React, { useEffect, useState } from 'react'
const Demo = ()=>{
const [num,setNum] = useState(0)
useEffect(() => {
// 第一次渲染完毕就执行 获取最新的状态值 每次组件更新后都会执行
setNum(10)
})
return <>
<div>{num}</div>
</>
}
export default Demo
- useEffect(callback,[])
- 设置了 但是没有依赖 只有第一次渲染完毕后 才会执行callback 每一次视图更新完毕后 不再执行callback
- 类似componentDidMount
const Demo = ()=>{ const [num,setNum] = useState(0) useEffect(() => { // 第一次渲染完毕就执行 获取最新的状态值 每次组件更新后不再执行 setNum(10) },[]) return <> <div>{num}</div> </> } export default Demo
- useEffect(callback,[依赖的状态[多个]])
- 第一次渲染完毕后会执行callback
- 当依赖的状态值(或者多个状态值中的一个发生变化)也会触发callback执行
- 依赖没有发生变化 组件更新变化的时候 callback不会执行
const Demo = ()=>{ console.log("渲染了") const [num,setNum] = useState(0) const [num2,setNum2] = useState(0) useEffect(() => { // 第一次渲染完毕就执行 获取最新的状态值 console.log("渲染了num",num) //setNum(10) },[num]) useEffect(() => { // 第一次渲染完毕就执行 获取最新的状态值 console.log("渲染了num2",num) //setNum(10) },[num2]) const handle = () => { setNum(num + 1); }; const handle2 = () => { setNum2(num2 + 1); }; return <> <div>{num}</div> <div>{num2}</div> <button onClick={handle}>新增</button> <button onClick={handle2}>新增</button> </> } export default Demo
- useEffect(()=> return ()=>{})
- 返回的函数,会在组件释放的时候执行
- 如果组件更新 会把上一次返回的函数执行 「可以“理解为”上一次渲染的组件释放了」
)
const Demo = ()=>{
const [num,setNum] = useState(0)
useEffect(() => {
// 获取的是上一次的状态值
return ()=>{
console.log("useEffect函数执行了") //输出为上一次的值
}
},[])
const handle = () => {
setNum(num + 1);
};
return <>
<div>{num}</div>
<button onClick={handle}>新增</button>
</>
}
export default Demo
```
useEffect必须在函数的最外层上下文中调用,不能把其嵌入到条件判断、循环等操作语句中
```
不能把其嵌入到条件判断、循环等操作语句中
if (num > 5) {
useEffect(() => {
console.log('OK');
});
}
```
useEffect如果设置返回值,则返回值必须是一个函数「代表组件销毁时触发」;下面案例 中,callback经过async的修饰,返回的是一个promise实例,不符合要求!!
```
useEffect(async () => {
let data = await queryData();
console.log('成功:', data);
}, []);
```
# useLayoutEffect
useLayoutEffect会阻塞浏览器渲染真实DOM,优先执行Effect链表中的callback;
useEffect不会阻塞浏览器渲染真实DOM,在渲染真实DOM的同时,去执行Effect链表中的callback;
+ useLayoutEffect设置的callback要优先于useEffect去执行!!
+ 在两者设置的callback中,依然可以获取DOM元素「原因:真实DOM对象已经创建了,区别只是浏览器是否渲染」
+ 如果在callback函数中又修改了状态值「视图又要更新」
+ useEffect:浏览器肯定是把第一次的真实已经绘制了,再去渲染第二次真实DOM
+ useLayoutEffect:浏览器是把两次真实DOM的渲染,合并在一起渲染的
视图更新的步骤:
第一步:基于babel-preset-react-app把JSX编译为createElement格式
第二步:把createElement执行,创建出virtualDOM
第三步:基于root.render方法把virtualDOM变为真实DOM对象「DOM-DIFF」
useLayoutEffect阻塞第四步操作,先去执行Effect链表中的方法「同步操作」
useEffect第四步操作和Effect链表中的方法执行,是同时进行的「异步操作」
第四步:浏览器渲染和绘制真实DOM对象
const Demo = ()=>{
const [num,setNum] = useState(0)
useLayoutEffect(() => {
console.log("第一个输出")
},[num])
useEffect(() => {
console.log("第二个输出")
},[num])
const handle = () => {
setNum(num + 1);
};
return <>
<div>{num}</div>
<button onClick={handle}>新增</button>
</>
}
export default Demo
```