ahooks 中那些控制“时机”的hook都是怎么实现的?
本文是深入浅出 ahooks 源码系列文章的第五篇,该系列已整理成文档-地址。觉得还不错,给个 star 支持一下哈,Thanks。
本文来探索一下 ahooks 是怎么封装 React 的一些执行“时机”的?
Function Component VS Class Component
学习类似 React 和 Vue 这种框架,对它们生命周期的掌握都是必须的,我们需要清楚的知道我们代码的执行顺序,并且在不同的阶段执行不同操作的代码,比如需要挂载完成之后才去获取 dom 的值,否则可能会获取不到相应的值。
Class Component
使用过 React 的 Class Component 的同学,就会知道其组件生命周期会分成三个状态:
- Mounting(挂载):已插入真实 DOM
- Updating(更新):正在被重新渲染
- Unmounting(卸载):已移出真实 DOM
简单版如下所示:
其中每个状态中还会按顺序调用不同的方法,对应的详细如下(这里不展开说):
可以通过官方提供这个网站查看详情
可以看到,会有非常多的生命周期方法,而且在不同的版本,生命周期方法还不同。
Function Component
到了 Function Component ,会发现没有直接提及生命周期的概念,它是更彻底的状态驱动,它只有一个状态,React 负责将状态渲染到视图中。
对于 Function Component 来说由状态到页面渲染只有三步:
- 输入状态(prop、state)
- 执行组件的逻辑,并在
useEffect/useLayoutEffect
中订阅副作用 - 输出UI(Dom节点)
重点是第二步,React 通过 useEffect/useLayoutEffect 订阅副作用。Class Component 中的生命周期都可以通过 useEffect/useLayoutEffect 来实现。它们两个的功能非常相似,我们这里看下 useEffect。
使用 useEffect 相当于告诉 React 组件需要在渲染后执行某些操作,React 将在执行 DOM 更新之后调用它。React 保证了每次运行 useEffect 的时候,DOM 已经更新完毕。这就实现了 Class Component 中的 Mounting(挂载阶段)。
当状态发生变化的时候,它能够执行对应的逻辑、更行状态并将结果渲染到视图中,这就完成了 Class Component 中的 Updating(更新阶段)。
最后通过在 useEffect 中返回一个函数,它便可以清理副作用。它的规则是:
- 首次渲染不会进行清理,会在下一次渲染,清除上一次的副作用。
- 卸载阶段也会执行清除操作。
通过返回一个函数,我们就能实现 Class Component 中的 Unmounting(卸载阶段)。
基于 useEffect/useLayoutEffect,ahooks 做了一些封装,能够让你更加清晰的知道你的代码执行时机。
LifeCycle - 生命周期
useMount
只在组件初始化时执行的 Hook。
useEffect 依赖假如为空,只会在组件初始化的时候执行。
// 省略部分代码
const useMount = (fn: () => void) => {
// 省略部分代码
// 单纯就在 useEffect 基础上封装了一层
useEffect(() => {
fn?.();
}, []);
};
export default useMount;
useUnmount
useUnmount,组件卸载(unmount)时执行的 Hook。
useEffect 可以在组件渲染后实现各种不同的副作用。有些副作用可能需要清除,所以需要返回一个函数,这个函数会在组件卸载的时候执行。
const useUnmount = (fn: () => void) => {
const fnRef = useLatest(fn);
useEffect(
// 在组件卸载(unmount)时执行的 Hook。
// useEffect 的返回值中执行函数
() => () => {
fnRef.current();
},
[],
);
};
export default useUnmount;
useUnmountedRef
获取当前组件是否已经卸载的 Hook。
通过判断有没有执行 useEffect 中的返回值判断当前组件是否已经卸载。
// 获取当前组件是否已经卸载的 Hook。
const useUnmountedRef = () => {
const unmountedRef = useRef(false);
useEffect(() => {
unmountedRef.current = false;
// 如果已经卸载,则会执行 return 中的逻辑
return () => {
unmountedRef.current = true;
};
}, []);
return unmountedRef;
};
export default useUnmountedRef;
Effect
这里只会讲官方文档
Effect
下面的几个,有部分是定时器、防抖节流等,咱们后面的系列具体分析。
useUpdateEffect 和 useUpdateLayoutEffect
useUpdateEffect 和 useUpdateLayoutEffect 的用法跟 useEffect 和 useLayoutEffect 一样,只是会忽略首次执行,只在依赖更新时执行。
实现思路:初始化一个标识符,刚开始为 false。当首次执行完的时候,置为 true。只有标识符为 true 的时候,才执行回调函数。
// 忽略首次执行
export const createUpdateEffect: (hook: effectHookType) => effectHookType =
(hook) => (effect, deps) => {
const isMounted = useRef(false);
// for react-refresh
hook(() => {
return () => {
isMounted.current = false;
};
}, []);
hook(() => {
// 首次执行完时候,设置为 true,从而下次依赖更新的时候可以执行逻辑
if (!isMounted.current) {
isMounted.current = true;
} else {
return effect();
}
}, deps);
};
useDeepCompareEffect和useDeepCompareLayoutEffect
用法与 useEffect 一致,但 deps 通过 lodash isEqual 进行深比较。
通过 useRef 保存上一次的依赖的值,跟当前的依赖对比(使用 lodash 的 isEqual),并将对比结果作为 useEffect 的依赖项,从而决定回调函数是否执行。
const depsEqual = (aDeps: DependencyList, bDeps: DependencyList = []) => {
return isEqual(aDeps, bDeps);
};
const useDeepCompareEffect = (effect: EffectCallback, deps: DependencyList) => {
// 通过 useRef 保存上一次的依赖的值
const ref = useRef<DependencyList>();
const signalRef = useRef<number>(0);
// 判断最新的依赖和旧的区别
// 如果相等,则变更 signalRef.current,从而触发 useEffect 中的回调
if (!depsEqual(deps, ref.current)) {
ref.current = deps;
signalRef.current += 1;
}
useEffect(effect, [signalRef.current]);
};
useUpdate
useUpdate 会返回一个函数,调用该函数会强制组件重新渲染。
返回的函数通过变更 useState 返回的 state,从而促使组件进行更新。
import { useCallback, useState } from 'react';
const useUpdate = () => {
const [, setState] = useState({});
// 通过设置一个全新的状态,促使 function 组件更新
return useCallback(() => setState({}), []);
};
export default useUpdate;
总结与思考
在我们写代码的时候需要清晰的知道,组件的生命周期是怎样的,我们代码的执行顺序、执行的时机是怎样的。
在 Function Component 中,使用 useEffect/useLayoutEffect 完成了 Class Components 生命周期的职责。ahooks 也是基于这两个封装了常见的代码执行时机,使用这些 hook,可以让我们的代码更加具有可读性以及逻辑更加清晰。
ahooks 中那些控制“时机”的hook都是怎么实现的?的更多相关文章
- 轻松了解Spring中的控制反转和依赖注入(二)
紧接上一篇文章<轻松了解Spring中的控制反转和依赖注入>讲解了SpringIOC和DI的基本概念,这篇文章我们模拟一下SpringIOC的工作机制,使我们更加深刻的理解其中的工作. 类 ...
- Spring框架中IoC(控制反转)的原理(转)
原文链接:Spring框架中IoC(控制反转)的原理 一.IoC的基础知识以及原理: 1.IoC理论的背景:在采用面向对象方法设计的软件系统中,底层实现都是由N个对象组成的,所有的对象通过彼此的合作, ...
- ASP.Net中后台控制页面提示信息的显示方式
ASP.Net中后台控制页面提示信息的显示方式 用于删除或修改成功后的显示:(背景No空白) ScriptManager.RegisterStartupScript(this, typeof(Pa ...
- Hadoop中的控制脚本
1.提出问题 在上篇博文中,提到了为什么要配置ssh免密码登录,说是Hadoop控制脚本依赖SSH来执行针对整个集群的操作,那么Hadoop中控制脚本都是什么东西呢?具体是如何通过SSH来针对整个集群 ...
- Spring中的控制反转和依赖注入
Spring中的控制反转和依赖注入 原文链接:https://www.cnblogs.com/xxzhuang/p/5948902.html 我们回顾一下计算机的发展史,从最初第一台计算机的占地面积达 ...
- 关于.NET中的控制反转(三)- 依赖注入之 Autofac
一.Autofac简介 Autofac和其他容器的不同之处是它和C#语言的结合非常紧密,在使用过程中对你的应用的侵入性几乎为零,更容易与第三方的组件集成.Autofac的主要特性如下: 组件侵入性为零 ...
- Eclipse中自动提示的方法参数都是arg0,arg1的解决方法
Eclipse中自动提示的方法参数都是arg0,arg1,就不能根据参数名来推断参数的含义,非常不方便. 解决方法:Preferences->Java->Installed JREs,发现 ...
- 最近调试HEVC中码率控制, 发现HM里面一个重大bug
最近调试HEVC中码率控制, 发现里面一个重大bug! 码率控制中有这么一个函数: Int TEncRCGOP::xEstGOPTargetBits( TEncRCSeq* encRCSeq, Int ...
- C++中所有的变量和函数都必须有类型
/* C++中所有的变量和函数都必须有类型 C语言中的默认类型在C++中是不合法的 函数f的返回值是什么类型,参数又是什么类型? 函数g可以接受多少个参数? */ //更换成.cpp就会报错 f(i) ...
随机推荐
- A* K短路
注:\(A*\) 求解K短路效率极其低下,时间复杂度\(O(nklog\ n)\),空间视题目而定,因为本质是爆搜,可求解数据范围较小的题目. 我们使用\(A*\)求解k短路: 首先需要预处理出估价函 ...
- Zookeeper分布式锁实现Curator十一问
前面我们剖析了Redisson的源码,主要分析了Redisson实现Redis分布式锁的15问,理清了Redisson是如何实现的分布式锁和一些其它的特性.这篇文章就来接着剖析Zookeeper分布式 ...
- 【Spring】AOP实现原理(三):创建代理
AbstractAutoProxyCreator 在AbstractAutoProxyCreator的wrapIfNecessary方法中,调用getAdvicesAndAdvisorsForBean ...
- sharepoint 配置失败,已引发类型为System.ArgumentException的异常。其他异常信息:domainName参数不支持指定的值。
解决方法:在域控制器中加入sharepoint计算机,设置为administrators组中
- 重学ES系列之新增的几个循环方法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 二:动手实操SpringBoot-使用Spring Initializr创建项目
使用 Spring Initializr 初始化 Spring Boot 项目 Spring Initializr 从本质上说就是一个Web应用程序,它能为你构建Spring Boot项目结构. 虽然 ...
- Pisa-Proxy 之 SQL 解析实践
SQL 语句解析是一个重要且复杂的技术,数据库流量相关的 SQL 审计.读写分离.分片等功能都依赖于 SQL 解析,而 Pisa-Proxy 作为 Database Mesh 理念的一个实践,对数据库 ...
- MySql查看索引以及各字段含义
查看表的索引: show index from userInfo(表名) show index from 数据库名.表名 查看某表某一列上的索引使用下面的SQL语句: show index from ...
- Collections集合工具类的方法_sort(List)和sort(List,Comparator)方法
Comparator比较器 我们还是先研究这个方法 public static <T> void sort(List<T> list):将集合中元素按照默认规则排序. 不过这次 ...
- 一题多解,ASP.NET Core应用启动初始化的N种方案[下篇]
[接上篇]"天下大势,分久必合,合久必分",ASP.NET应用通过GenericWebHostService这个承载服务被整合到基于IHostBuilder/IHost的服务承载系 ...