1. 引出useEffect

上篇文章我们讲解了useState,满足了函数式组件可以拥有自己的状态,状态更改,实现页面重新渲染;在类组件中,拥有很多生命周期,在不同的阶段执行不同的生命周期函数,那么函数式组件有没有类似于类组件生命周期的Hook呢?答案是有的,就是我们今天要讲解的useEffect这个钩子。

2. useEffect用法

useEffect不同的写法有不同的作用,下面根据不同的写法给大家讲解下其各自的作用。

没有设置依赖:useEffect(callback)

作用:

  1. 函数式组件第一次渲染完毕后,执行useEffect中的callback回调函数,等价于类组件中的componentDidMount;
  2. 在组件每一次更新完毕后,也会执行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, [依赖的状态(可以有多个状态)])

作用:

  1. 函数式组件第一次渲染完毕后会执行callback;
  2. 当依赖的状态值(或者多个依赖状态中的一个)发生改变,也会触发callback执行;
  3. 依赖的状态如果没有变化,在组件更新的时候,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 () => {}
},[依赖项])

作用:

  1. 在组件释放的时候执行;
  2. 如果组件更新,会把上一次返回的函数执行,可以"理解为"上一次渲染的组件释放了,类似于类组件的componentWillUnmount;
  3. 如果第二个参数是个空数组,不会执行;
  4. 如果没有第二个参数,每次组件更新会把上一次返回的函数执行;
  5. 如果存在依赖的状态,依赖的状态发生改变后,组件更新,会把上一次返回的函数执行。

代码示例如下:

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();
}, []);