当前位置:网站首页>Review Set/Map basics with these two hooks
Review Set/Map basics with these two hooks
2022-08-01 21:03:00 【GopalFeng】
本文是深入浅出 ahooks The tenth article in the source code series,这个系列的目标主要有以下几点:
- 加深对 React hooks 的理解.
- 学习如何抽象自定义 hooks.构建属于自己的 React hooks 工具库.
- 培养阅读学习源码的习惯,工具库是一个对源码阅读不错的选择.
今天我们来聊聊 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 的比较[1].
我们来看下 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 整体架构篇[2]
- 如何使用插件化机制优雅的封装你的请求hook [3]
- ahooks 是怎么解决 React 的闭包问题的?[4]
- ahooks 是怎么解决用户多次提交问题?[5]
- ahooks 中那些控制“时机”的hook都是怎么实现的?[6]
- 如何让 useEffect 支持 async...await?[7]
- 如何让定时器在页面最小化的时候不执行?[8]
- 记录第一次给开源项目提 PR[9]
参考资料
[1]Objects 和 maps 的比较: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Map#objects_%E5%92%8C_maps_%E7%9A%84%E6%AF%94%E8%BE%83
[2]大家都能看得懂的源码(一)ahooks 整体架构篇: https://juejin.cn/post/7105396478268407815
[3]如何使用插件化机制优雅的封装你的请求hook : https://juejin.cn/post/7105733829972721677
[4]ahooks 是怎么解决 React 的闭包问题的?: https://juejin.cn/post/7106061970184339464
[5]ahooks 是怎么解决用户多次提交问题?: https://juejin.cn/post/7106461530232717326
[6]ahooks 中那些控制“时机”的hook都是怎么实现的?: https://juejin.cn/post/7107189225509879838
[7]如何让 useEffect 支持 async...await?: https://juejin.cn/post/7108675095958126629
[8]如何让定时器在页面最小化的时候不执行?: https://juejin.cn/post/7109399243202232357
[9]记录第一次给开源项目提 PR: https://juejin.cn/post/7110144695098933284
边栏推荐
猜你喜欢
随机推荐
微服务负载均衡器Ribbon
案例:MySQL主从复制与读写分离
附录A printf、varargs与stdarg A.2 使用varargs.h来实现可变参数列表
Digital twin Beijing the imperial palace, yuan universe is the process of tourism
New graduate students, great experience in reading English literature, worthy of your collection
关键字搜索:“淘宝商品 API ”
Pytorch框架学校记录11——搭建小实战完整细节
JS提升:如何中断Promise的链式调用
MySQL 中出现的字符编码错误 Incorrect string value: ‘\x\x\x\x‘ for column ‘x‘
Use WeChat official account to send information to designated WeChat users
Protocol Buffer usage
tiup mirror init
OSG Notes: Set DO_NOT_COMPUTE_NEAR_FAR to manually calculate far and near planes
string
如何让定时器在页面最小化的时候不执行?
AQS原理和介绍
MySQL Syntax Basics
面试官:大量请求 Redis 不存在的数据,从而打倒数据库,有什么方案?
C陷阱与缺陷 第7章 可移植性缺陷 7.7 除法运算时发生的截断
Godaddy domain name resolution is slow and how to use DNSPod resolution to solve it