本文是深入浅出 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. 开源流程引擎camunda如何扩展

    ​  市场上基于Java语言的开源工作流引擎有:osworkflow.jbpm.activiti.flowable.camunda等,其中osworkflow.jbpm流程引擎已经过时,目前主流的开源 ...

  2. 入坑KeePass(三)安全设置完后后留存

    1.文件> 数据库设置 > 安全 迭代次数改成500000 2.工具 > 选项 2.1.安全 2.2.策略 2.3.集成 2.4高级

  3. C++ 炼气期之数据是主角

    1. 前言 数据在程序中的重要性,怎么强调都不为过,程序的本质就是通过提供数据处理逻辑,把数据从一种状态变成另一种状态的过程.处理逻辑一定是有针对性的,针对的是数据本身的特性. 只有了解了数据本身的内 ...

  4. alertmanager集群莫名发送resolve消息的问题探究

    alertmanager集群莫名发送resolve消息的问题探究 术语 告警消息:指一条告警 告警恢复消息:指一条告警恢复 告警信息:指告警相关的内容,包括告警消息和告警恢复消息 问题描述 最近遇到了 ...

  5. 20.LVS负载均衡群集—NAT模式实例

    LVS负载均衡群集-NAT模式实例 目录 LVS负载均衡群集-NAT模式实例 群集引用概述 群集的含义 问题 解决方法 企业群集分类 群集的三种类型 负载均衡群集(Load Balance Clust ...

  6. JS:三目运算符

    语法:条件表达式?表达式1:表达式0 注:当条件表达式为true则选择表达式1,反之false则选择表达式0 例: var a = 0; var b = 1; re=a>b?a:b consol ...

  7. SAP SD-Invoice 销售发票

    针对销售订单的发票流程: 1. 事务码:VF01(个别生成系统发票) 创建开票凭证(发票)/  VF04 开具系统发票(可把多个item 合并成一张系统发票) 2. 事务码:VF02 修改发票, 释放 ...

  8. Kali信息收集

    前言 渗透测试最重要的阶段之一就是信息收集,需要收集关于目标主机的基本细腻些.渗透测试人员得到的信息越多,渗透测试成功的概率也就越高. 一.枚举服务 1.1 DNS枚举工具DNSenum DNSenu ...

  9. 深入解析kubernetes controller-runtime

    Overview controller-runtime 是 Kubernetes 社区提供可供快速搭建一套 实现了controller 功能的工具,无需自行实现Controller的功能了:在 Kub ...

  10. C++ 练气期之一文看懂字符串

    C++ 练气期之细聊字符串 1. 概念 程序不仅仅用于数字计算,现代企业级项目中更多流转着充满了烟火气的人间话语.这些话语,在计算机语言称为字符串. 从字面上理解字符串,类似于用一根竹签串起了很多字符 ...