提示react hook——你可能不是“我”所认识的useEffect前言class组件生命周期模拟useEffect & useLayoutEffect区别
据说,这个hook可以模拟class组件的三个生命周期
前言
官网已经介绍过,这里再啰嗦一次。
useEffect
是一个用来执行副作用hook,第一个参数传入一个函数,每一次render之后执行副作用和清除上一次副作用,该函数的返回值就是清除函数。第二个参数是一个数组,传入内部的执行副作用函数需要的依赖,当这几个依赖有一个要更新,effect里面也会重新生成一个新的副作用并执行副作用。如果没有更新,则不会执行。如果第二个参数不传,那么就是没有说明自己有没有依赖,那就是每次render该函数组件都执行。
很明显,
useEffect
第一个参数可以模仿
didmount
、
didupdate
,它的返回值可以模仿
willunmount
class组件生命周期模拟
"模仿生命周期,useEffect第二个参数传个空数组,无依赖,只执行一次,相当于didmount。如果要区分生命周期,不传第二个参数,每次都会跑,相当于didupdate。加个mount标记一下,里面用if判断一下,即可以达到模拟生命周期的效果"
很多人都会想到这个办法模拟,于是我们试一下看看:
let mount;
function useForceUpdate() {
const [_, forceUpdate] = useState(0);
return () => forceUpdate(x => x + 1);
function UnmountTest() {
useEffect(() => {
if (!mount) {
mount = true;
console.log('did mount')
} else {
console.log('did update')
return () => {
mount = false;
console.log('unmount')
const forceUpdate = useForceUpdate();
return (<div>
我是随时被抛弃的
<button onClick={forceUpdate}>强制更新</button>
</div>);
function State() {
const [count, setCount] = useState(20);
const handleCount = useCallback(() => {
setCount(count => count + 1)
}, [])
return (
{count}
<button onClick={handleCount}>count+1</button>
{(count % 2) && <UnmountTest />}
复制代码
当count是奇数,那就展示
UnmountTest
,组件里面也有一个更新组件的方法。按照逻辑,
useEffect
不传第二个参数,保证每次渲染都执行。然后加一个标记,标记第一次是挂载。于是运行一波看看
- 点一下count+1,展示组件,打印didmount
- 再点一下count,删掉组件,打印unmount
符合预期,?
- 点一下count+1,展示组件,打印didmount
- 点一下强制更新,打印unmount、didmount,再点,还是一样
?️,什么鬼,居然不符合预期
useEffect是用来执行副作用,每一次render,将会清除上一次副作用、执行本次副作用(如果有依赖或者不传入依赖数组)这个hook是以一个副作用为单位,当然也可以多次使用
这样子说,每一次都是unmount、didmount,的确是符合这个逻辑,和"想当然"的那种模拟生命周期是有点不一样的。这样子,我们拆成两个useEffect调用,就可以解决问题:
function UnmountTest() {
useEffect(() => {
if (mount) {
console.log('did update')
useEffect(() => {
if (!mount) {
console.log('did mount')
mount = true;
return () => {
console.log('unmount')
mount = false;
}, []);
const forceUpdate = useForceUpdate();
return (<div>
我是随时被抛弃的
<button onClick={forceUpdate}>强制更新</button>
</div>);
复制代码
这次,全都符合预期了,简直ojbk?
useEffect & useLayoutEffect区别
useEffect是异步的,useLayoutEffect是同步的
我们看一下,一次组件从挂载到重新渲染,两者的发生的时机:
从左到右表示时间线,红色的是异步的,红色框内是同步的,从上到下执行。
useEffect
是异步的,所谓的异步就是利用
requestIdleCallback
,在浏览器空闲时间执行传入的callback。大部分情况下,用哪一个都是一样的,如果副作用执行比较长,比如大量计算,如果是
useLayoutEffect
就会造成渲染阻塞。这只是一个case,我们可以看一下这个神奇的定时器:
点击开始,开始计时,点击暂停就暂停。点击清0,暂停并且数字清零
function LYE() {
const [lapse, setLapse] = React.useState(0)
const [running, setRunning] = React.useState(false)
useEffect(
() => {
if (running) {
const startTime = Date.now() - lapse
const intervalId = setInterval(() => {
setLapse(Date.now() - startTime)
}, 2)
console.log(intervalId)
return () => clearInterval(intervalId)
[running],
function handleRunClick() {
setRunning(r => !r)
function handleClearClick() {
setRunning(false)
setLapse(0)
return (
<label>{lapse}ms</label>
<button onClick={handleRunClick}>
{running ? '暂停' : '开始'}
</button>
<button onClick={handleClearClick}>
暂停并清0
</button>