1. 引出useEffect
上篇文章我们讲解了useState,满足了函数式组件可以拥有自己的状态,状态更改,实现页面重新渲染;在类组件中,拥有很多生命周期,在不同的阶段执行不同的生命周期函数,那么函数式组件有没有类似于类组件生命周期的Hook呢?答案是有的,就是我们今天要讲解的useEffect这个钩子。
2. useEffect用法
useEffect不同的写法有不同的作用,下面根据不同的写法给大家讲解下其各自的作用。
没有设置依赖:useEffect(callback)
作用:
- 函数式组件第一次渲染完毕后,执行useEffect中的callback回调函数,等价于类组件中的componentDidMount;
- 在组件每一次更新完毕后,也会执行useEffect中的callback回调函数,等价于类组件中的componentDidUpdate。
代码示例如下:
import { useState, useEffect } from "react";
const Demo = () => {
const [num, setNum] = useState(0);
useEffect(() => {
console.log('@1', num)
});
const handle = () => {
setNum(num + 1);
}
return (
<>
<div>{ num }</div>
<button onClick={ handle }>增加</button>
</>
);
};
export default Demo;
Demo组件第一次渲染完毕后:@1 0;
第一次点击按钮后:@1 1;
第二次点击按钮后:@1 2;
…
设置但没有依赖项:useEffect((callback, [ ])
作用:
只有函数式组件第一次渲染完毕后,才会执行callback,每一次视图更新完毕后,callback就不再执行,类似于类组件的componentDidMount。
代码示例如下:
import { useState, useEffect } from "react";
const Demo = () => {
const [num, setNum] = useState(0);
useEffect(() => {
console.log('@2', num)
},[]);
const handle = () => {
setNum(num + 1);
}
return (
<>
<div>{ num }</div>
<button onClick={ handle }>增加</button>
</>
);
};
export default Demo;
Demo组件第一次渲染完毕后:@2 0;
第一次点击按钮后:不再执行callback;
第二次点击按钮后:不再执行callback;
…
设置并有依赖:useEffect(callback, [依赖的状态(可以有多个状态)])
作用:
- 函数式组件第一次渲染完毕后会执行callback;
- 当依赖的状态值(或者多个依赖状态中的一个)发生改变,也会触发callback执行;
- 依赖的状态如果没有变化,在组件更新的时候,callback不会执行。
代码示例如下:
import { useState, useEffect } from "react";
const Demo = () => {
const [num, setNum] = useState(0);
const [count, setCount] = useState(0);
useEffect(() => {
console.log('@3', num)
},[num]);
const handle = () => {
setNum(num + 1);
}
const handleClick = () => {
setCount(count + 1);
}
return (
<>
<div>{ num }</div>
<button onClick={ handle }>增加num</button>
<button onClick={ handleClick }>增加count</button>
</>
);
};
export default Demo;
Demo组件第一次渲染完毕后:@3 0;
第一次点击按钮执行handle后:@3 1;
第二次点击按钮执行handle后:@3 2;
第一次点击按钮执行handleClick后:不执行callback;
…
返回函数:useEffect(() => {
return () => {}
},[依赖项])
作用:
- 在组件释放的时候执行;
- 如果组件更新,会把上一次返回的函数执行,可以"理解为"上一次渲染的组件释放了,类似于类组件的componentWillUnmount;
- 如果第二个参数是个空数组,不会执行;
- 如果没有第二个参数,每次组件更新会把上一次返回的函数执行;
- 如果存在依赖的状态,依赖的状态发生改变后,组件更新,会把上一次返回的函数执行。
代码示例如下:
import { useState, useEffect } from "react";
const Demo = () => {
const [num, setNum] = useState(0);
useEffect(() => {
return () => {
console.log('@4', num); // num 获取的是上一次的状态值
}
}, [num]);
const handle = () => {
setNum(num + 1);
}
return (
<>
<div>{ num }</div>
<button onClick={ handle }>增加</button>
</>
);
};
export default Demo;
Demo组件第一次渲染完毕后:返回的函数没有执行;
点击按钮更改num:@4 0;
再次点击按钮更改num:@4 1;
…
3. useEffect注意事项
useEffect必须在函数的最外层上下文中调用,不能把其嵌入到条件判断、循环等操作语句中
错误示例:
if (num > 5) {
useEffect(() => {})
}
useEffect 只能是一个同步函数,不能使用 async
错误示例:
// 假设queryData返回Promise实例
useEffect(async () => {
let data = await queryData();
})
解决办法:
// 假设queryData返回Promise实例
useEffect(() => {
const next = async () => {
let data = await queryData();
}
next();
}, []);