useEffect与useLayoutEffect

useEffectuseLayoutEffect可以统称为Effect HookEffect Hook可以在函数组件中执行副作用操作,副作用是指函数或者表达式的行为依赖于外部环境,或者在这里可以理解为修改了某状态会对其他的状态造成影响,这个影响就是副作用,数据获取,设置订阅以及手动更改React组件中的DOM都属于副作用。

useEffect

useEffect Hook可以看做 componentDidMountcomponentDidUpdatecomponentWillUnmount这三个生命周期函数的组合,但是使用多个Effect实现关注点分离,也就是说useEffect的粒度更低,可以将各个关注的位置分离处理副作用。

既然是对componentDidMountcomponentDidUpdatecomponentWillUnmount这三个生命周期函数的组合,那么我们也可以使用useEffect将其分离,首先对于componentDidMountcomponentWillUnmount,也就是想执行只运行一次的 effect(仅在组件挂载和卸载时执行),由于不存在任何依赖,那么对于第二个参数就是一个空的数组。如果省略了第二个参数的话,那么在组件的初始化和更新都会执行,一般情况下是并不希望这样的,因为Hooks的设计,每次setState都会重新执行组件函数,这样的话副作用函数就会频繁执行,所以通常来说还是尽量不要省略第二个参数。回到生命周期,通常如果在组件建立时建立了一个定时器,那么我们希望在组件销毁的时候将定时器销毁来避免内存泄露,那么在useEffect中返回一个函数调用去关闭定时器即可,在这里我们的关注点可以集中在一起而不用再分开两个生命周期去写了。

import { useEffect, useState } from "react";
import "./styles.css"; export default function App() {
const [count, setCount] = useState(0); useEffect(() => {
console.log("Component is mounted");
return () => console.log("Component is unmounted");
}, []); useEffect(() => {
console.log("Component is mounted or updated");
}) return (
<div>
<div>{count}</div>
<button onClick={() => setCount(count + 1)}>count + 1</button>
</div>
);
}

对于componentDidUpdate,之前如果是写class组件实现相同的功能的话,就需要在这个生命周期中嵌入很多的逻辑,使用useEffect就可以将各个关注点分离,分别处理其副作用,当然如果依然需要解除诸如订阅或者定时器等,依旧可以返回一个处理函数来处理。

import { useEffect, useState } from "react";
import "./styles.css"; export default function App() {
const [count, setCount] = useState(0); useEffect(() => {
console.log("Count is updated");
document.title = `count: ${count}`;
}, [count]); return (
<div>
<div>{count}</div>
<button onClick={() => setCount(count + 1)}>count + 1</button>
</div>
);
}

在文档中还指出请确保数组中包含了所有外部作用域中会随时间变化并且在effect中使用的变量,否则你的代码会引用到先前渲染中的旧变量。如果你传入了一个空数组[]effect内部的propsstate就会一直拥有其初始值。下面这个例子就会出现一个bug,在依赖数组中没有传递count,那么就会导致当effect执行时,创建的effect闭包会将count的值保存在该闭包当中,且初值为0,每隔一秒回调就会执行setCount(0 + 1),因此count永远不会超过1,此时如果我们将count加入到依赖数组中便可解决这个问题。对于这个问题,React提供了一个exhaustive-depsESLint规则作为eslint-plugin-react-hooks包的一部分,它会帮助你找出无法一致地处理更新的组件。

import { useEffect, useState } from "react";
import "./styles.css"; export default function App() {
const [count, setCount] = useState(0); useEffect(() => {
const id = setInterval(() => {
setCount(count + 1);
console.log(count + 1);
}, 1000);
return () => clearInterval(id);
}, []); // `count` 没有被指定为依赖 return (
<div>
<div>{count}</div>
<button onClick={() => setCount(count + 1)}>count + 1</button>
</div>
);
}

看起来和VueWatch很像,但是又不尽然相同,语法上的区别主要就在于useEffect可以监控多个属性的变化,Watch不行,当然Watch可以通过间接的方式实现,但是思想方面是不同的,Vue是监听值的变化而React是用以处理副作用。提到这个的主要原因是因为之前写Vue较多,就老想着通过Vue的角度来类比React的各项实现,感觉这样有好处也有弊端,好处就是很快能够上手,坏处就是很容易钻牛角尖,或者很容易陷入一个围城。有位大佬说的挺好的,你需要把Vue忘掉再来学习Hooks,虽然并不绝对但也很有道理。

当函数组件刷新渲染时,包含useEffect的组件整个运行过程如下:

  • 触发组件重新渲染,通过改变组件state或者组件的父组件重新渲染,导致子节点渲染。
  • 组件函数执行。
  • 组件渲染后呈现到屏幕上。
  • useEffect hook执行。

useLayoutEffect

useLayoutEffectuseEffect很像,函数签名也是一样,唯一的不同点就是useEffect是异步执行,而useLayoutEffect是同步执行的。当函数组件刷新渲染时,包含useLayoutEffect的组件整个运行过程如下:

  • 触发组件重新渲染,通过改变组件state或者组件的父组件重新渲染,导致子组件渲染。
  • 组件函数执行。
  • useLayoutEffect hook执行,React等待useLayoutEffect的函数执行完毕。
  • 组件渲染后呈现到屏幕上。

每日一题

https://github.com/WindrunnerMax/EveryDay

参考

https://zhuanlan.zhihu.com/p/348701319
https://zhuanlan.zhihu.com/p/259766064
https://segmentfault.com/a/1190000039087645
http://www.ptbird.cn/react-hoot-useEffect.html
https://react.docschina.org/docs/hooks-effect.html
https://pengfeixc.com/blog/605af93600f1525af762a725
https://react.docschina.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies

useEffect与useLayoutEffect的更多相关文章

  1. React的useEffect与useLayoutEffect执行机制剖析

    引言 useEffect和useLayoutEffect是React官方推出的两个hooks,都是用来执行副作用的钩子函数,名字类似,功能相近,唯一不同的就是执行的时机有差异,今天这篇文章主要是从这两 ...

  2. useEffect 和 useLayoutEffect浅析

    执行时期的区别 useEffect 回调函数的执行时期 useEffect为异步执行,执行时期为 触发状态更新(如:setState,forceUpdate) React渲染函数执行(render) ...

  3. React中useLayoutEffect和useEffect的区别

    重点: 1.二者函数签名相同,调用方式是一致的 2. 怎么简单进行选择: 无脑选择useEffect,除非运行效果和你预期的不一致再试试useLayoutEffect 区别详解:useEffect是异 ...

  4. 精读《setState 做了什么》

    1 引言 setState 是 React 框架最常用的命令,它是用来更新状态的,这也是 React 框架划时代的功能. 但是 setState 函数是 react 包导出的,他们又是如何与 reac ...

  5. Preact(React)核心原理详解

    原创: 宝丁 玄说前端 本文作者:字节跳动 - 宝丁 一.Preact 是什么 二.Preact 和 React 的区别有哪些? 三.Preact 是怎么工作的 四.结合实际组件了解整体渲染流程 五. ...

  6. React Hooks用法大全

    前言 在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们依赖 ...

  7. React源码 commit阶段详解

    转: React源码 commit阶段详解 点击进入React源码调试仓库. 当render阶段完成后,意味着在内存中构建的workInProgress树所有更新工作已经完成,这包括树中fiber节点 ...

  8. PReact10.5.13源码理解之hook

    hook源码其实不多,但是实现的比较精巧:在diff/index.js中会有一些optison.diff这种钩子函数,hook中就用到了这些钩子函数.   在比如options._diff中将curr ...

  9. react之react Hooks

    函数组件,没有 class 组件中的 componentDidMount.componentDidUpdate 等生命周期方法,也没有 State,但这些可以通过 React Hook 实现. Rea ...

  10. ahooks 中那些控制“时机”的hook都是怎么实现的?

    本文是深入浅出 ahooks 源码系列文章的第五篇,该系列已整理成文档-地址.觉得还不错,给个 star 支持一下哈,Thanks. 本文来探索一下 ahooks 是怎么封装 React 的一些执行& ...

随机推荐

  1. 【TouchGFX 】使用 CubeMX 创建 TouchGFX 工程时 LCD 显示为雪花屏

    经几个晚上折腾,修改大量的LTDC时钟.时序,FMC时序等,结果还是一样,耐心与好使的工程仔细对比,发现是时钟源配置问题,真是冤,聊以此以示纪念 实质上是没有分清有源和无源晶振 无源晶振又被叫做 谐振 ...

  2. 软考下午科目——第三章——UML分析与设计

    UML分析与设计 大纲要求: 学会面向对象的分析与设计,掌握UML描述方法 UML基础知识 面向对象的分析与设计 面向对象方法是一种运用对象.类.继承.封装.聚合.关联.消息.多态性等概念来构造系统的 ...

  3. [转帖]美国出口管制第六番 ECCN编码的藏宝图之旅

    https://zhuanlan.zhihu.com/p/585040344 哈喽大家好,这里是大话合规 一旦明确物项受EAR管制(大前提) 下一步就是对物项进行编码 @#¥%&* 这篇文章蜗 ...

  4. [转帖]配置cri-docker使kubernetes1.24以docker作为运行时

    从kubernetes 1.24开始,dockershim已经从kubelet中移除,但因为历史问题docker却不支持kubernetes主推的CRI(容器运行时接口)标准,所以docker不能再作 ...

  5. [转帖]内存管理参数zone_reclaim_mode分析

    zone_reclaim_mode 官方解释 调整方法 调整的影响 官方解释 最近在性能优化,看到了zone_reclaim_mode参数,记录备用 zone_reclaim_mode: Zone_r ...

  6. Tidb异名恢复Mysql数据库的过程

    Tidb异名恢复Mysql数据库的过程 背景 先说坑: TiDB备份恢复的方式 1. mysqldump + mysql source 的方式. 2. mydumper + loader tidb 的 ...

  7. 两千字讲明白java中instanceof关键字的使用!

    写在开头 在过往的内容中,我们讲了不少的Java关键字,比如final.static.this.super等等,Java中的关键字非常之多,下图是整理的关键字集合 而我们今天要学习的就是其中的inst ...

  8. with(上下文管理器)的用法

    with语句可以自动管理上下文资源,不论什么原因(成功或失败)跳出with语句,都能保证文件正确关闭,并 释放资源,不用手动去close掉资源 1.with语句中有两个内置方法__enter__和__ ...

  9. Go中字符串处理:fmt.Sprintf与string.Builder的比较

    在Go语言中,我们通常会遇到两种主要的方式来处理和操作字符串:使用fmt.Sprintf函数和string.Builder类型.尽管两者都可以实现字符串的格式化和连接,但它们在性能和用法上有一些关键区 ...

  10. GitHub要求2FA?不慌,有它们帮你

    近日,GitHub宣布,到 2023 年底,所有用户都必须要启用双因素身份验证 (2FA),不能只用密码啦. 正如GitHub的首席安全官Mike Hanley所指出的那样,保护开发者账号是确保软件供 ...