ahooks 源码解读系列

3,197 阅读2分钟

前阵子了解到了 ahooks 这个库(好像了解的有点晚🤦‍♂️),粗略看了看文档,感觉这个东西有点赞啊,尤其是 useVirtualList 更是让我开了眼界,此处应有黑蒜姐的哇~。原来 hook 不止是可以封装逻辑,甚至是 ui 也能一起处理,真的是学到了。

小丑竟是我自己

终究是一人抗下所有

好吧,那就废话少说,开始整活。下面所说的源码为 ahooks v2.10.3 版本

为了和代码原始注释区分,个人理解部分使用 ///开头,此处和 三斜线指令没有关系,只是为了做区分。

先说结论

个人认为大部分的 hook 都是很实用的,很多都是用来解决实际生产中会遇到的需求。大家先粗略看看都有什么 hook,然后在实际生产过程中有遇到类似的需求可以直接拿来就用。

由于目前项目是 react + antd 技术栈,所以优先推荐的有:useAntdTable 、useMount、useUpdateEffect、useUrlState、useLocalStorageState 和 useSafeState 等。其他的感觉是针对细分场景的,可以不用花过多的时间去了解,遇到了再看就行了。

首先我们来看看 Advanced 部分的 hooks。

Advanced

useCreation

升级版的 useRef,用于创建常量。

import { useRef } from 'react';

export default function useCreation<T>(factory: () => T, deps: any[]) {
  const { current } = useRef({
    deps, /// 存储上一次的依赖数组
    obj: undefined as undefined | T, /// 工厂函数生产的目标值
    initialized: false, /// 是否已经初始化
  });
  if (current.initialized === false || !depsAreSame(current.deps, deps)) {
    current.deps = deps;
    current.obj = factory();
    current.initialized = true;
  }
  return current.obj as T;
}
/// 通过 === 按顺序对依赖数组逐项对比,并且只会对比第一层,所以如果依赖是个对象或者数组,想要更新必须要改变引用地址
function depsAreSame(oldDeps: any[], deps: any[]): boolean {
  if (oldDeps === deps) return true;
  for (let i = 0; i < oldDeps.length; i++) {
    if (oldDeps[i] !== deps[i]) return false;
  }
  return true;
}

useCreation 主要是利用了 useRef 的一个重要性质:返回的 ref 对象在组件的整个生命周期内保持不变。这种用法在我一开始看 react 的文档时是没有注意到的,还以为是和以前一样仅仅用作 dom 访问。这里我再详细介绍一下这个性质,因为 ahooks 中大量使用了这个特性,如果没有很好的理解这个原理,阅读起来总会感觉有点不明所以。

useRef

useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。而且当 ref 对象内容发生变化时,useRef不会通知你。变更 .current 属性不会引发组件重新渲染。

所以说useRef()ref 属性更有用。它可以很方便地保存任何可变值,其类似于在 class 中使用实例字段的方式。

但是如果这个值需要进行懒加载计算,useRef() 就不太适合了,因为useRef 不会useState 那样接受一个特殊的函数重载。这也是为什么会有上面的useCreation自定义 hook 的原因,useCreation可以说是如何惰性创建昂贵的对象?的一种抽象实现。

以上内容由于本人水平问题难免有误,欢迎大家进行讨论反馈。

第一次在掘金发文章,也不熟悉情况,欢迎大家多多点赞评论转发!