本文是深入浅出 ahooks 源码系列文章的第十篇,该系列已整理成文档-地址。觉得还不错,给个 star 支持一下哈,Thanks。

今天我们来聊聊 ahooks 中对 Map 和 Set 类型进行状态管理的 hook,顺便复习一下 Set 和 Map 这两种数据类型。

useMap

管理 Map 类型状态的 Hook。

先回顾以下 Map 的概念。Map 对象保存键值对,并且能够记住键的原始插入顺序任何值(对象或者基本类型)都可以作为一个键或一个值。

Object 和 Map 很类似。它们都允许你按键存取一个值、删除键、检测一个键是否绑定了值。因此过去我们一直都把对象当成 Map 使用。

但是,在一些场景下,使用 Map 是更优的选择,以下是一些常见的点:

  • 键值的类型。一个 Map 的键可以是任意值,包括函数、对象或任意基本类型。一个 Object 的键必须是一个 String 或是 Symbol。
  • 需要保证键值的顺序。Map 中的键是有序的。因此,当迭代的时候,一个 Map 对象以插入的顺序返回键值。虽然 Object 的键目前是有序的,但并不总是这样,而且这个顺序是复杂的。因此,最好不要依赖属性的顺序
  • Size。Map 的键值对个数可以轻易地通过 size 属性获取。Object 的键值对个数只能手动计算。比如遍历对象属性,计算它的个数。
  • 性能。Map 在频繁增删键值对的场景下表现更好。Object 在频繁添加和删除键值对的场景下未作出优化。

更多,可以看 Objects 和 maps 的比较

我们来看下 ahooks 做了哪些封装,同时回顾以下 Map 的一些基础 API 用法。

首先是默认值的设置,通过 Map 构造函数 new Map() 创建 Map 对象。入参为默认值。

function useMap<K, T>(
// 传入默认的 Map 参数
initialValue?: Iterable<readonly [K, T]>,
) {
const getInitValue = () => {
return initialValue === undefined ? new Map() : new Map(initialValue);
}; const [map, setMap] = useState<Map<K, T>>(() => getInitValue());
// 省略代码...
}

set 方法。添加 Map 新的 key 和 value 或者更新 key 的值,因为 React 是不可变数据,需要要返回一个全新的值,所以需要创建一个新的 Map 对象。

通过 Map 的 set 方法,在 Map 对象中设置与指定的键 key 关联的值 value,并返回 Map 对象。

// 添加 map
const set = (key: K, entry: T) => {
setMap((prev) => {
const temp = new Map(prev);
temp.set(key, entry);
return temp;
});
};

remove 方法。通过 Map 的 delete 方法,移除 Map 对象中指定的键值对,如果键值对存在并成功被移除,返回 true,否则返回 false。调用 delete 后再调用 Map.prototype.has(key) 将返回 false。

// 移除
const remove = (key: K) => {
setMap((prev) => {
const temp = new Map(prev);
temp.delete(key);
return temp;
});
};
  • setAll 方法。传入一个全新的 Map 对象,直接覆盖旧的 Map 对象。
  • reset 方法。重置 Map 对象为初始值。在 Map 中有一个 clear 的方法,它移除 Map 对象中所有的键值对,相比 clear,reset 方法更贴近我们的需求。
  • get 方法,通过 Map 的 get 方法,返回与 key 关联的值,若不存在关联的值,则返回 undefined。
// 生成一个新的 Map 对象
const setAll = (newMap: Iterable<readonly [K, T]>) => {
setMap(new Map(newMap));
};
// 重置
const reset = () => setMap(getInitValue());
// 获取
const get = (key: K) => map.get(key);

对于一些其他没有副作用的方法,ahooks 没有封装,我觉得是合理的,这些在开发者想用的时候,直接调用就可以了。

  • has(key)。返回一个布尔值,用来表明 Map 对象中是否存在与 key 关联的值
  • keys()。返回一个新的迭代对象,其中包含 Map 对象中所有的键,并以插入 Map 对象的顺序排列
  • values()。返回一个新的迭代对象,其中包含 Map 对象中所有的值,并以插入 Map 对象的顺序排列
  • entries()。返回一个新的迭代对象,其为一个包含 Map 对象中所有键值对的 [key, value] 数组,并以插入 Map 对象的顺序排列

useSet

管理 Set 类型状态的 Hook。

直接看代码。

默认值的设置,通过 new Set() 构造函数,创建一个新的 Set 对象

function useSet<K>(initialValue?: Iterable<K>) {
const getInitValue = () => {
return initialValue === undefined ? new Set<K>() : new Set(initialValue);
}; const [set, setSet] = useState<Set<K>>(() => getInitValue());
// 省略一些代码
}

add 方法添加一个元素。调用 Set 的 add 方法,在 Set 对象尾部添加一个元素。返回该 Set 对象。

const add = (key: K) => {
if (set.has(key)) {
return;
}
setSet((prevSet) => {
const temp = new Set(prevSet);
temp.add(key);
return temp;
});
};

remove 方法移除一个元素。调用 Set 的 delete(value) 方法,移除 Set 中与这个值相等的元素,返回 Set.prototype.has(value) 在这个操作前会返回的值(即如果该元素存在,返回 true,否则返回false)。Set.prototype.has(value) 在此后会返回 false。

// 移除
const remove = (key: K) => {
if (!set.has(key)) {
return;
}
setSet((prevSet) => {
const temp = new Set(prevSet);
temp.delete(key);
return temp;
});
};

reset 方法,重置 Set 回默认值。其对应的 Set 的 clear 方法,会移除Set对象内的所有元素

// 重置
const reset = () => setSet(getInitValue());

其他 Set 方法:

  • entries()。返回一个新的迭代器对象,该对象包含 Set 对象中的按插入顺序排列的所有元素的值的 [value, value] 数组。为了使这个方法和 Map 对象保持相似, 每个值的键和值相等。
  • has(value)。返回一个布尔值,表示该值在 Set 中存在与否。
  • keys() 和 values()。都返回一个新的迭代器对象,该对象包含 Set 对象中的按插入顺序排列的所有元素的值。
  • forEach(callbackFn[, thisArg])。按照插入顺序,为 Set 对象中的每一个值调用一次 callBackFn。如果提供了thisArg参数,回调中的 this 会是这个参数

思考与总结

ES6 中的 Map 和 Set 两种数据结构,弥补了 JavaScript 之前的一些不足,比如 Object 对象只能是 string 或者 Symbol 类型。另外,提供了某些情况下更便捷的操作方式,比如数组去重,我们可以直接 new Set([...arr])

现在越来越多的场景使用了 Map 和 Set,ahooks 对这两者的封装都比较简单,更多的是一些有副作用(修改到原 Map 和 Set)操作的封装。看这部分的源码,就当做小小复习基础知识吧。

本文已收录到个人博客中,欢迎关注~

大家都能看得懂的源码 - ahooks useSet 和 useMap的更多相关文章

  1. 大家都能看得懂的源码 - ahooks 是怎么处理 DOM 的?

    本文是深入浅出 ahooks 源码系列文章的第十三篇,该系列已整理成文档-地址.觉得还不错,给个 star 支持一下哈,Thanks. 本篇文章探讨一下 ahooks 对 DOM 类 Hooks 使用 ...

  2. 大家都能看得懂的源码(一)ahooks 整体架构篇

    本文是深入浅出 ahooks 源码系列文章的第一篇,该系列已整理成文档-地址.觉得还不错,给个 star 支持一下哈,Thanks. 第一篇主要介绍 ahooks 的背景以及整体架构. React h ...

  3. 大家都能看得懂的源码 - 如何封装 cookie/localStorage/sessionStorage hook?

    本文是深入浅出 ahooks 源码系列文章的第九篇,该系列已整理成文档-地址.觉得还不错,给个 star 支持一下哈,Thanks. 今天来看看 ahooks 是怎么封装 cookie/localSt ...

  4. 大家都能看得懂的源码 - 那些关于DOM的常见Hook封装(一)

    本文是深入浅出 ahooks 源码系列文章的第十四篇,该系列已整理成文档-地址.觉得还不错,给个 star 支持一下哈,Thanks. 上一篇我们探讨了 ahooks 对 DOM 类 Hooks 使用 ...

  5. 大家都能看得懂的源码之ahooks useInfiniteScroll

    本文是深入浅出 ahooks 源码系列文章的第十七篇,该系列已整理成文档-地址.觉得还不错,给个 star 支持一下哈,Thanks. 简介 useInfiniteScroll 封装了常见的无限滚动逻 ...

  6. 大家都能看得懂的源码 - 那些关于DOM的常见Hook封装(二)

    本文是深入浅出 ahooks 源码系列文章的第十五篇,该系列已整理成文档-地址.觉得还不错,给个 star 支持一下哈,Thanks. 本篇接着针对关于 DOM 的各个 Hook 封装进行解读. us ...

  7. 大家都能看得懂的源码之 ahooks useVirtualList 封装虚拟滚动列表

    本文是深入浅出 ahooks 源码系列文章的第十八篇,该系列已整理成文档-地址.觉得还不错,给个 star 支持一下哈,Thanks. 简介 提供虚拟化列表能力的 Hook,用于解决展示海量数据渲染时 ...

  8. 如何读懂Framework源码?如何从应用深入到Framework?

    如何读懂Framework源码? 首先,我也是一个应用层开发者,我想大部分有"如何读懂Framework源码?"这个疑问的,应该大都是应用层开发. 那对于我们来讲,读源码最大的问题 ...

  9. <转>如何高效快速看懂Android源码

    原网址:http://jingyan.baidu.com/article/574c5219ca78ed6c8d9dc12a.html 在Android系统上工作了一段时间,经常会遇到题目中的问题,下面 ...

随机推荐

  1. Flink使用Pod Template将状态快照(Checkpoint、Savepoint)存储在NFS

    背景 Flink 版本 1.13.3,使用 native k8s 部署模式,原采用 HDFS 作为状态快照(Checkpoint.Savepoint)的存储地址,但是由于仅使用了其 HDFS 作为状态 ...

  2. 用STM32玩SR04(测距、串口显示、OLED显示)

    目录 用STM32玩SR04(测距.串口显示.OLED显示) 超声波模块使用 SR04初始化 SR04使用串口打印数据 SR04使用OLED来传输数据,并显示在OLED上 用STM32玩SR04(测距 ...

  3. CabloyJS部署了一套演示站点

    为了方便大家快速体验和了解CabloyJS的风格和特性,全新部署了一套演示站点.对于初次接触CabloyJS的开发者,不用下载新建项目,就可以直接体验CabloyJS了 在线演示 场景 链接/二维码 ...

  4. PyTorch DataSet Normalization torchvision.transforms.Normalize()

         特征缩放, 在这种情况下,我们不仅仅考虑是一个值的数据集,我们考虑的是具有多个特征和相关的值的样本或元素的数据集. 假如正在处理一个人的数据集,           归一化数据集有许多不同的 ...

  5. VR技术赋能五大领域,不止高级,更高效!

    除了VR游戏.VR影视作品,究竟还有哪些产业领域会应用到VR技术并为生活带来改变呢?今天就帮大家好好梳理一下~ VR赋能交通,不只是高级 最近在网上看到了VR考驾照的新闻,网友都赞叹,现在学车都这么高 ...

  6. 文件上传漏洞靶场分析 UPLOAD_LABS

    文件上传漏洞靶场(作者前言) 文件上传漏洞 产生原理 PASS 1) function checkFile() { var file = document.getElementsByName('upl ...

  7. python-将print内容保存到文件

    通过sys.stdout得到print输出的内容,再进行保存 import sys class Logger(object): def __init__(self, file_path: str = ...

  8. 如何通过WinDbg获取方法参数值

    引入 我们在调试的过程中,经常会通过查看方法的输入与输出来确定这个方法是否异常.那么我们要怎么通过 WinDbg 来获取方法的参数值呢? WinDbg 中主要包含三种命令:标准命令.元命令(以 . 开 ...

  9. windows如何结束某个端口的进程

    netstat -ano | findstr 端口号 (查询端口号被哪个进程占用) tasklist | findstr 进程PID (根据PID找到进程名称) taskkill -PID 进程PID ...

  10. Linux系列之查找命令

    前言 Linux 有四个常用的查找命令:locate.whereis.which 和 find.本文介绍它们的区别和简单用法. locate命令 这个命令将检查你的整个文件系统,并找到该关键词的每一次 ...