一、简介
React hooks提供的api,用于弥补函数组件没有生命周期的缺陷。
1. useEffect
useEffect 基础介绍:
useEffect(()=>{
return destory
},dep)
-
useEffect 第一个参数 callback, 返回的 destory , destory 作为下一次callback执行之前调用,用于清除上一次 callback 产生的副作用。
-
第二个参数作为依赖项,是一个数组,可以有多个依赖项,依赖项改变,执行上一次callback 返回的 destory ,和执行新的 effect 第一个参数 callback 。
-
对于 useEffect 执行, React 处理逻辑是采用异步调用 ,对于每一个 effect 的 callback, React 会向 setTimeout回调函数一样,放入任务队列,等到主线程任务完成,DOM 更新,js 执行完成,视图绘制完毕,才执行。所以 effect 回调函数不会阻塞浏览器绘制视图。
useEffect 基础用法:
import React, { useState, useRef, useEffect } from "react";
import { Button } from "antd";
import axios from "axios";
const App = (props) => {
// 电影数据
const [cinemaList, setCinemaList] = useState([]);
// 获取dom
const divRef = useRef();
const [number, setNumber] = useState(0);
/* 模拟事件监听处理函数 */
const handleResize = () => {
console.log("Resize");
};
/* useEffect使用 ,这里如果不加限制 ,会是函数重复执行,陷入死循环*/
useEffect(() => {
/* 请求数据 */
axios({
url: "https://m.maizuo.com/gateway?cityId=110100&ticketFlag=1&k=3085018",
method: "get",
headers: {
"X-Client-Info": '{"a":"3000","ch":"1002","v":"5.2.0","e":"1646314068530784943341569"}',
"X-Host": "mall.film-ticket.cinema.list",
},
})
.then((res) => {
console.log(res.data);
setCinemaList(res.data.data.cinemas);
})
.catch((err) => {
console.log(err);
});
/* 定时器 延时器等 */
const timer = setInterval(() => console.log("定时器"), 5000);
/* 操作dom */
console.log(divRef.current); /* div */
/* 事件监听等 */
window.addEventListener("resize", handleResize);
/* 此函数用于清除副作用 */
return function () {
clearInterval(timer);
window.removeEventListener("resize", handleResize);
};
/* 只有当props->a和state->number改变的时候 ,useEffect副作用函数重新执行,
如果此时数组为空[],证明函数只有在初始化的时候执行一次相当于componentDidMount */
}, [props.a,number]);
return (
<div ref={divRef}>
<ul>
{cinemaList.map((item) => (
<li key={item.cinemaId}>{item.name}</li>
))}
</ul>
<Button type="primary" onClick={() => setNumber(1)}>
{number}
</Button>
</div>
);
};
export default App;
如上在 useEffect(来源)中做的功能如下:
- ① 请求数据。
- ② 设置定时器,延时器等。
- ③ 操作 dom , 在 React Native 中可以通过 ref 获取元素位置信息等内容。
- ④ 注册事件监听器, 事件绑定,在 React Native 中可以注册 NativeEventEmitter 。
- ⑤ 还可以清除定时器,延时器,解绑事件监听器等。
2. useLayoutEffect
useLayoutEffect 基础介绍:
useLayoutEffect 和 useEffect 不同的地方是采用了同步执行,那么和useEffect有什么区别呢?
-
首先 useLayoutEffect 是在 DOM 更新之后,浏览器绘制之前,这样可以方便修改 DOM,获取 DOM 信息,这样浏览器只会绘制一次,如果修改 DOM 布局放在 useEffect ,那 useEffect 执行是在浏览器绘制视图之后,接下来又改 DOM ,就可能会导致浏览器再次回流和重绘。而且由于两次绘制,视图上可能会造成闪现突兀的效果。
-
useLayoutEffect callback 中代码执行会阻塞浏览器绘制。
useEffect 基础用法:
const DemoUseLayoutEffect = () => {
const target = useRef()
useLayoutEffect(() => {
/*我们需要在dom绘制之前,移动dom到制定位置*/
const { x ,y } = getPositon() /* 获取要移动的 x,y坐标 */
animate(target.current,{ x,y })
}, []);
return (
<div >
<span ref={ target } className="animate"></span>
</div>
)
}
3. useInsertionEffect
useInsertionEffect 基础介绍:
useInsertionEffect 是在 React v18 新添加的 hooks ,它的用法和 useEffect 和 useLayoutEffect 一样。那么这个 hooks 用于什么呢?
在介绍 useInsertionEffect 用途之前,先看一下 useInsertionEffect 的执行时机。
import React, { useEffect, useLayoutEffect, useInsertionEffect } from "react";
const App = () => {
useEffect(() => {
console.log("useEffect 执行");
}, []);
useLayoutEffect(() => {
console.log("useLayoutEffect 执行");
}, []);
useInsertionEffect(() => {
console.log("useInsertionEffect 执行");
}, []);
};
export default App;
打印:
可以看到 useInsertionEffect 的执行时机要比 useLayoutEffect 提前,useLayoutEffect 执行的时候 DOM 已经更新了,但是在 useInsertionEffect 的执行的时候,DOM 还没有更新。本质上 useInsertionEffect 主要是解决 CSS-in-JS 在渲染中注入样式的性能问题。这个 hooks 主要是应用于这个场景,在其他场景下 React 不期望用这个 hooks 。
useInsertionEffect使用场景:
import React, {useInsertionEffect } from "react";
const App = () => {
useInsertionEffect(() => {
/* 动态创建 style 标签插入到 head 中 */
const style = document.createElement("style");
style.innerHTML = `
.css-in-js{
color: pink;
font-size: 12px;
}
`;
console.log("style: " , style);
document.head.appendChild(style);
}, []);
return <div className="css-in-js"> useInsertionEffect使用场景 </div>;
};
export default App;
4. useEffect 函数的总结, 如有错误, 感谢指正
useEffect(()=> { return ()=> {}},[])
- 参数 1 effect 函数 => 发送请求 修改数据 ...
- 参数 2 effect 函数依赖参数 决定effect 的更新方式
- return 返还函数 ==> 组件要卸载的时候做一些事情
执行机制
- 挂载阶段 => 1 先执行useEffect 函数 , 并把effect 函数存入队列等待执行
- 挂载完成 => 执行effect 函数队列
更新阶段
- 执行新的的useEffect 函数 , 并肩effect 函数存入队列等待等待执行
- 执行返还函数队列, 并观察返还函数书否有依赖参数, 有依赖参数, 追踪依赖参数是否改变, 改变执行, 没有改变不执行
- 执行effect 函数队列, 观察effect 函数是否有依赖参数,有依赖参数, 追踪依赖参数是否改变, 改变执行, 没有改变不执行
卸载阶段
- 执行返还函数队列