useEffect和useLayoutEffect的区别
useLayoutEffect
是 useEffect
的一个版本,在浏览器重新绘制屏幕之前触发,可以理解为布局渲染副作用
。
useLayoutEffect(setup, dependencies?)
setup 处理副作用的函数。
setup
函数选择性返回一个清理(cleanup)
函数。在将组件首次添加到 DOM 之前,React 将运行setup
函数。在每次因为依赖项变更而重新渲染后,React 将首先使用旧值运行cleanup
函数(如果你提供了该函数),然后使用新值运行setup 函数
。在组件从 DOM 中移除之前,React 将最后一次运行cleanup 函数
。
可选 dependencies setup 代码中引用的所有响应式值的列表。
响应式值包括props、state
以及所有直接在组件内部声明的变量和函数。如果你的代码检查工具 配置了 React,那么它将验证每个响应式值都被正确地指定为一个依赖项。依赖项列表必须具有固定数量的项,并且必须像[dep1, dep2, dep3]
这样内联编写。React 将使用Object.is
来比较每个依赖项和它先前的值。如果省略此参数,则在每次重新渲染组件之后,将重新运行副作用函数。
注意:useLayoutEffect
可能会影响性能。尽可能使用 useEffect
。
用法:在浏览器重新绘制屏幕前计算布局
返回值: useLayoutEffect
返回 undefined
。
注意事项:
useLayoutEffect` 是一个 Hook,因此只能在 组件的顶层 或自己的 Hook
中调用它。不能在循环或者条件内部调用它。如果你需要的话,抽离出一个组件并将副作用处理移动到那里。
- 当
StrictMode
启用时,React 将在真正的 setup 函数首次运行前,运行一个额外的开发专有的setup + cleanup
周期。这是一个压力测试,确保 cleanup 逻辑“映照”到setup
逻辑,并停止或撤消setup
函数正在做的任何事情。如果这导致一个问题,请实现清理函数。- 如果你的一些依赖项是组件内部定义的对象或函数,则存在这样的风险,即它们将 导致
Effect
重新运行的次数多于所需的次数。要解决这个问题,请删除不必要的 对象 和 函数 依赖项。你还可以 抽离状态更新 和 非响应式逻辑 到
Effect
之外。Effect
只在客户端上运行,在服务端渲染中不会运行。useLayoutEffect
内部的代码和所有计划的状态更新阻塞了浏览器重新绘制屏幕。如果过度使用,这会使你的应用程序变慢。如果可能的话,尽量选择useEffect
。
比如,我们实现一个tooltip的操作。在tooltip渲染之前我们需要计算鼠标在DOM 的位置,如果空间足够,则在DOM元素上方渲染,反之则在下方渲染。在tooltip渲染之前我们需要计算这些高度,然后再实现渲染。这就需要用到useLayoutEffect这个hooks
function ShowTooltip() {
const ref = useRef(null);
const [tooltipHeight, setTooltipHeight] = useState(0); // 你还不知道真正的高度
useLayoutEffect(() => {
const { height } = ref.current.getBoundingClientRect();
setTooltipHeight(height); // 现在重新渲染,你知道了真实的高度
}, []);
// ... 在下方的渲染逻辑中使用 tooltipHeight ...
}
useLayoutEffect和useEffect的区别:
useLayoutEffect
会计算DOM布局,在渲染最终效果,会等,会有顺序的渲染(先渲染初始值,再渲染计算后的值,最终渲染实际的效果,会排先后),所以会阻塞最终渲染的效果,会阻塞浏览器的渲染,但不会引起闪烁(不会显示初始值的渲染效果,只显示最终想要的效果)
import { useRef, useLayoutEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import TooltipWrapper from './TooltipWrapper.js';
export default function Tooltip({ children, targetDom }) {
const ref = useRef(null);
const [tooltipHeight, setTooltipHeight] = useState(0);
useLayoutEffect(() => {
const { height } = ref.current.getBoundingClientRect();
setTooltipHeight(height);
}, []);
let tooltipX = 0;
let tooltipY = 0;
if (targetDom !== null) {
tooltipX = targetDom.left;
tooltipY = targetDom.top - tooltipHeight;
if (tooltipY < 0) {
// 它不适合上方,因此把它放在下面。
tooltipY = targetDom.bottom;
}
}
return createPortal(
<TooltipWrapper x={tooltipX} y={tooltipY} contentRef={ref}>
{children}
</TooltipWrapper>,
document.body
);
}
useEffect
不会顺序渲染,它是同步进行,不会等,所以会显示初始效果,在显示最终效果,渲染2次,不会阻塞渲染,但是会闪烁(会显示初始值)
import { useRef, useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import TooltipWrapper from './TooltipWrapper.js';
export default function Tooltip({ children, targetDom }) {
const ref = useRef(null);
const [tooltipHeight, setTooltipHeight] = useState(0);
useEffect(() => {
const { height } = ref.current.getBoundingClientRect();
setTooltipHeight(height);
}, []);
let tooltipX = 0;
let tooltipY = 0;
if (targetDom !== null) {
tooltipX = targetDom.left;
tooltipY = targetDom.top - tooltipHeight;
if (tooltipY < 0) {
// 它不适合上方,因此把它放在下面。
tooltipY = targetDom.bottom;
}
}
return createPortal(
<TooltipWrapper x={tooltipX} y={tooltipY} contentRef={ref}>
{children}
</TooltipWrapper>,
document.body
);
}
原文地址:https://blog.csdn.net/qyl_0316/article/details/137542888
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!