logo

请解释 useEffect?与 useLayoutEffect 的区别?

2023年2月2日

useEffect 是 React 中常用的一个 Hook,也是前端面试中经常被问到的 React 面试题,包括如何使用 useEffect、useEffect 的执行时机等。另一个与 useEffect 相似的 Hook 是 useLayoutEffect,这两者的比较也是 React 面试的高频题。

useEffect 是什么?

useEffect 是一个用于连接外部系统的 React Hook。 React 的函式元件需要是纯函式,但如果我们需要执行具有副作用(side effect) 的操作,例如:请求 API、使用第三方函式库,我们就需要将这些代码放在 useEffect 中执行。外部系统例如是:服务器端、浏览器提供的 API 或是第三方函式库。因为这部分不是由 React 本身处理的,所以称为外部系统。

useEffect 使用方法规则

只能在顶部呼叫

useEffect 也是一种 hook,因此只能在顶层呼叫,不能在回圈、条件式或者巢状的 function 中使用,想了解细节的朋友,可以前往这篇文章 《为什么只能在最顶端层呼叫 Hook?从 useState 实作原理来回答》

useEffect 接受两个参数:setup function 和 dependencies(可选)

  1. setup function

    setup function:setup function 包含如何连结外部系统的代码,如果需要清除逻辑,可以在 setup function 中回传一个清除 function。

  2. dependencies dependencies 参数是可选的数组,可以传入 props、state 或元件中任何使用的变数。 React 会使用Object.is 算法来进行比较, (想了解Object.is 的细节可以阅读此篇文章 《在 JavaScript 当中,==、=== 与Object.is ()的区别》)。如果 dependencies 中任意一个值与前一次不同,则此 useEffect 会重新执行。 。

import { useEffect } from "react";
import { createConnection } from "./blog.js";

function Article({ articleId }) {
  const [serverUrl, setServerUrl] = useState("https://blog.com/0");

  useEffect(() => {
    const connection = createConnection(serverUrl, articleId);
    connection.connect();

    // 回传 cleanip function
    return () => {
      connection.disconnect();
    };
  }, [serverUrl, articleId]);
  // ...
}

useEffect 执行时机

  1. 当元件被加入时 (mount),useEffect 会被第一次执行。
  2. 当每次元件重新渲染时,如果 dependencies 的值有改变,先将旧的 props 和 state 执行 cleanup function,再带着新的 props 和 state 执行 setup function。
  3. cleanup function 的代码,会在元件生命周期结束 (unmount) 时,执行最后一次。

使用 useEffect,画面重新渲染时都会闪烁要怎么解?

解法:尝试将 useEffect 换成使用 useLayoutEffect

以下这一段我们会讨论跟 useEffect 很相近的 Hook - useLayoutEffect

而标题已经点出来,useLayoutEffect 通常会拿来处理当使用 useEffect 但画面会出现闪烁的情境,详细原因下面会提到。

useLayoutEffect 是什么?

useLayoutEffect 其实是 useEffect 的一种版本,传入的参数也一样,只是执行时机不一样,它会在浏览器重绘 (repaints) 前执行。

useLayoutEffect 可能会造成性能的问题,因为在 useLayoutEffect 里的代码会阻碍浏览器重绘 (repaints) ,太频繁使用可能会造成整个应用程式缓慢。因此通常会建议先使用 useEffect,如果不能解决问题,才会选择使用 useLayoutEffect

useEffectuseLayoutEffect 比较

以下提供一个代码范例,可以明显感到这两者差别。 (备注:以下代码范例是为了凸显这两者差别,实际上开发并不会这样写)

代码

import { useEffect, useLayoutEffect, useState } from "react";

export default function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    if (count === 0) {
      const randomNum = 1 + Math.random() * 1000;
      setCount(randomNum);
    }
  }, [count]);

  return <div onClick={() => setCount(0)}>{count}</div>;
}

当我们执行上方代码时,连续点击 div 区块,会看到画面产生闪烁。

原因是,当你每次点击 div,此时 count 会被更新为 0,画面会重新渲染变为 0,同时,因为 count 被更新,也会触发 useEffect 执行。所以在重绘完成之后, useEffect 执行并把 count 更新为另一串随机数字,画面也会再渲染一次,因为两次渲染时间很快,所以造成闪烁。

useLayoutEffect 的差异是什么 ?

假设我们把上方代码的useEffect 换成useLayoutEffect,当你每次点击 div,此时 count 会被更新为 0,但这时,画面不会被重新渲染变为 0,而是先等待 useLayoutEffect 内的代码执行完毕之后,state 已经更新为新的随机数字,这时画面才进行重绘。

延伸问题

如果不指定 dependencies ,useEffect 什么时候会执行?

此 effect 会是在每次重新渲染元件后重新执行

如果 dependencies 中有 object 会怎么样?

下方代码的 options 物件会在元件每次重新渲染时,都是不同的物件,所以这个 useEffect 可能会在每次渲染时都重新执行,因为在 dependencies 中的 options 有改变。

function ChatRoom({ articleId }) {
  const [article, setArticle] = useState(null);

	// options 会在每次渲染时重新创建
  const options = {
    serverUrl: 'https://localhost:1234',
    articleId: articleId
  };

  useEffect(() => {
    const data = getArticle(options);
    setArticle(data)

	// options 每次渲染时值都不同,因此触发 useEffect 执行
  }, [options]);

如果要避免上方代码造成不必要触发 useEffect,其实可以把动态的物件放在 Effect 中,并把 dependencies 中的物件改为 string 或 number,如下。

function ChatRoom({ articleId }) {
  const [article, setArticle] = useState(null);

  useEffect(() => {
	  const options = {
	    serverUrl: 'https://localhost:1234',
	    articleId: articleId
	  };

    const data = getArticle(options);
    setArticle(data)

  }, [articleId]);

什么时候使用 useLayoutEffect

如上方说到,useLayoutEffect 频繁使用会损害应用程式的效能,只有在一些特别情况才建议使用。以下提供一个使用情境 (此例子来自于 React Docs Beta)。

假设今天我们要设计一个工具提示元件(tooltip),它会依照不同条件出现在某元素的上方、下方或旁边,这种设计条件代表说,我们会需要知道此元素准确的高度位置,才能判断要将 tooltip 显示在哪里。

React 的渲染步骤可以拆分为下

如果是使用useEffect 的话,Tooltip 的位置,可能是从 0 变到 10 的位置,这会造成画面闪烁、使用者体验不佳,如果是使用useLayoutEffect,React 则会在重绘前,就重新计算正确的位置,才渲染画面。

代码可参考此连结 React Docs Beta。

玻璃钢生产厂家合肥环保玻璃钢雕塑九江玻璃钢雕塑设计上海进口玻璃钢雕塑厂家直供潍坊玻璃钢树脂雕塑陕西渭南市大型玻璃钢雕塑荆州玻璃钢景观雕塑佛山玻璃钢校园雕塑节庆商场美陈厂家直销肇庆玻璃钢动物雕塑生产合肥玻璃钢雕塑厂家诚信企业新乡室外不锈钢玻璃钢仿铜雕塑荆州玻璃钢雕塑定做平南玻璃钢泡沫雕塑价格玻璃钢浮雕雕塑厂家定做四川小区玻璃钢雕塑定做三国卡通玻璃钢雕塑福安玻璃钢花盆销售金昌玻璃钢景观雕塑安装江门玻璃钢卡通雕塑厂家常用商场美陈销售企业武汉玻璃钢鹿雕塑价格玻璃钢彩绘雕塑制作过程鸡西市玻璃钢雕塑定制玻璃钢雕塑过程南京哪里有玻璃钢雕塑厂家赣州玻璃钢雕塑制作北京季节性商场美陈销售公司鲁权屯镇玻璃钢雕塑苏州耐高温玻璃钢花盆泰州玻璃钢雕塑厂香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化