You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
_submit 方法:对 form 表单校验后,根据当前 form 表单数据、分页等筛选条件进行对表格数据搜索。
const_submit=(initPagination?: TParams[0])=>{setTimeout(()=>{// 先进行校验validateFields().then((values={})=>{// 分页的逻辑constpagination=initPagination||{pageSize: options.defaultPageSize||10,
...(params?.[0]||{}),current: 1,};// 假如没有 form,则直接根据分页的逻辑进行请求if(!form){// @ts-ignorerun(pagination);return;}// 获取到当前所有 form 的 Data 参数// record all form dataallFormDataRef.current={
...allFormDataRef.current,
...values,};// @ts-ignorerun(pagination,values,{allFormData: allFormDataRef.current,
type,});}).catch(err=>err);});};
constshift=useCallback(()=>{// remove keys if necessarytry{keyList.current=keyList.current.slice(1,keyList.current.length);}catch(e){console.error(e);}setList(l=>l.slice(1,l.length));},[]);
sortList - 校准排序
为什么要校对?
// 校准排序constsortList=useCallback((result: T[])=>result.map((item,index)=>({key: index, item }))// add index into obj.sort((a,b)=>getIndex(a.key)-getIndex(b.key))// sort based on the index of table.filter(item=>!!item.item)// remove undefined(s).map(item=>item.item),// retrive the data[],);
本文是全网最全 ahooks 源码分析篇之一,该系列已整理成文档-地址。觉得还不错,给个 star 支持一下哈,Thanks。
本系列主要是针对 ahooks 的
v3.3.13
版本进行源码解读。个人 folk 仓库可见 详情。本文来看下 ahooks 针对常见的业务场景做了哪些封装。通过对这些 hook 的分析和思考,希望能够给大家在日常工作中遇到的场景有所帮助,也欢迎给 ahooks 提 RFC。
useAntdTable
文档地址
详细代码
首先调用 usePagination 处理分页的逻辑。
然后处理列表页筛选 Form 表单的逻辑,这里支持 Antd v3 和 Antd v4 版本。
获取当前表单值,
form.getFieldsValue
或者form.getFieldInstance
:校验表单逻辑
form.validateFields
:重置表单
form.setFieldsValue
:修改表单类型,支持
'simple'
和'advance'
。初始化的表单数据可以填写 simple 和 advance 全量的表单数据,开发者可以根据当前激活的类型来设置表单数据。修改 type 的时候会重置 form 表单数据。_submit
方法:对 form 表单校验后,根据当前 form 表单数据、分页等筛选条件进行对表格数据搜索。另外当表格触发 onChange 方法的时候,也会进行请求:
初始化的时候,会根据当前是否有缓存的数据,有则根据缓存的数据执行请求逻辑。否则,通过
manual
和ready
判断是否需要进行重置表单后执行请求逻辑。最后,将请求返回的数据通过 dataSource、 pagination、loading 透传回给到 Table 组件,实现 Table 的数据以及状态的展示。以及将对 Form 表单的一些操作方法暴露给开发者。
usePagination
文档地址
详细代码
首先通过 useRequest 处理请求,service 约定返回的数据结构为
{ total: number, list: Item[] }
。其中 useRequest 的 defaultParams 参数第一个参数为
{ current: number, pageSize: number }
。并根据请求的参数以及返回的 total 值,得出总的页数。还有 refreshDeps 变化,会重置 current 到第一页「
changeCurrent(1)
」,并重新发起请求,一般你可以把 pagination 依赖的条件放这里。重点看下 onChange 方法:
最后返回请求的结果以及 pagination 字段,包含所有分页信息。另外还有操作分页的函数。
小结:usePagination 默认用法与 useRequest 一致,但内部封装了分页请求相关的逻辑。返回的结果多返回一个 pagination 参数,包含所有分页信息,及操作分页的函数。
缺点就是对 API 请求参数有所限制,比如入参结构必须为
{ current: number, pageSize: number }
,返回结果为{ total: number, list: Item[] }
。useCountDown
文档地址
详细代码
其实现原理就是通过定时器 setInterval 进行设置倒计时,为负值时,停止倒计时。
初始化 state:
其中 calcLeft 方法是计算目标时间和当前时间之间还有多少毫秒,入参是目标时间:
通过定时器进行倒计时:
最后针对返回的结果进行格式化:
useCounter
文档地址
详细代码
其实现原理很简单,就是暴露相应方法对数值进行管理。比如增加、减少、重置等方法。
初始化数据:
getTargetValue 获取目标数值,必须大于等于 min,小于等于 max。否则直接取 min 或者 max。
setValue 设置值,可以留意其入参类型设置。
inc/dec/set/reset 方法都是调用的 setValue 方法。
useDynamicList
文档地址
详细代码
其原理上就是对数组常见操作进行了封装。有一些比较基础,直接看代码注释即可。
通过 useRef 返回同一个 ref 对象,每次设置的时候都自增 1,从而保证生成唯一的 key。keyList 中某个 item 的索引跟其对应源数据 item 索引保持一致。
通过 useState 设置初始化数据。
通过 splice 方法进行处理。
这里除了移除数组中的某项,还需要移除 keyList 中的值,只是这里还做了错误捕获,这个没想到会是什么场景。
为什么要校对?
useHistoryTravel
文档地址
详细代码
实现原理,其在内部维护了以下的数据结构。通过队列的方式维护过去和未来的列表。
直接将 present 值设置为初始值或者入参中的第一个值。并重置 future 和 past。
其对应的方法是 updateValue。直接看代码:
不管前进还是后退,都是调用
split
函数。不同的是前进则第二个参数传递的是 feature,后退则第二个参数传递的是 past。split 函数主要的作用将传入 targetArr,根据 step,分成当前状态、之前、未来的状态。
比如前进,出参为 2 和 [1,2,3,4],得到的结果是
{ _current: 2, _before: [1], _after: [3,4] }
。比如后退,出参为 -1,[1,2,3,4],得到的结果是
{ _current: 4, _before: [1, 2, 3], _after: [] }
。最终调用 _forward 和 _backward
useInfiniteScroll
文档地址
详细代码
简介
详细可看官网
注意:这里的无限滚动指的是常见的点击加载更多或者说下拉加载更加功能,而不是虚拟滚动,虚拟滚动后面会讲到。
实现原理
实现原理:使用了 useRequest hook 负责进行请求后台数据。其中 reloadAsync 对应 useRequest 的 runAsync,reload 对应 useRequest 的 run。前者返回 Promise,需要自行处理异常。后者内部已经做了异常处理。
另外假如传入 target 和 isNoMore 参数,通过监听 scroll 事件,判断是否滚动到指定的位置(支持设置 threshold 值-距离底部距离阈值),进行自动发起加载更多请求,从而实现滚动自动加载效果。
大概说完原理,来看代码。
具体实现
入参以及状态定义,可以直接看注释:
判断是否有数据:isNoMore 的入参是当前聚合后的 data。
通过 useRequest 处理请求,可以看到 onBefore、onSuccess、onError、onFinally、manual 等参数都是直接传到了 useRequest 中。
loadMore/loadMoreAsync 和 reload/reloadAsync 分别对应调用的是 useRequest 的 run 和 runAsync 函数。
并且当 reloadDeps 依赖发生变化的时候,会触发 reload,进行重置:
最后就是滚动自动加载的逻辑,通过 scrollHeight - scrollTop <= clientHeight + threshold 结果判断是否触底。
上面提到的三个重要的值 scrollTop,scrollHeight,clientHeight 可以看 utils 中的 rect。
对应的值分别为以下结果:
scrollTop
scrollHeight
clientHeight
useNetwork
文档地址
详细代码
实现原理是通过监听网络 online、offline、change 事件,并通过 navigator 的 connection 获取到网络的情况。
获取网络状态
主要是通过以下 getConnectionProperty 方法获取,可以直接看注释。
监听事件,更新网络情况
监听 online 事件,触发设置 online 为 true。
监听 offline 事件,触发设置 online 为 false。
监听网络波动,则对网络情况进行更新。
since 为最后一次更新的时间。
useSelections
文档地址
详细代码
实现原理,维护所有项的值 items 数组以及设置选择的元素 setSelected(Set 数据结构)。
基本都是数组和 Set 的一些基础操作。可以直接看代码:
useTextSelection
文档地址
详细代码
其实现原理主要是监听 mouseup 和 mousedown 事件。调用 window.getSelection() 方法 获取到 Selection 对象,并通过 getBoundingClientRect 方法获取到其位置信息。
selection 是一个 Selection 对象。 如果想要将 selection 转换为字符串,可通过连接一个空字符串("")或使用 String.toString() 方法。
监听和取消事件,在 mouseup 事件中,获取到选取的文本以及位置信息。在 mousedown 中清除之前的信息。
获取文本位置信息:
useVirtualList
文档地址
详细代码
简介
详情可见官网,文章源代码可以点击这里
实现原理
其实现原理监听外部容器的 scroll 事件以及其 size 发生变化的时候,触发计算逻辑算出内部容器的高度和 marginTop 值。
具体实现
其监听滚动逻辑如下:
其中 calculateRange 非常重要,它基本实现了虚拟滚动的主流程逻辑,其主要做了以下的事情:
变量很多,可以结合下图,会比较清晰理解:
代码如下:
其它就是这个函数的辅助函数了,包括:
最后暴露一个滚动到指定的 index 的函数,其主要是计算出该 index 距离顶部的高度 scrollTop,设置给外部容器。并触发 calculateRange 函数。
思考与总结
对于高度相对比较确定的情况,我们做虚拟滚动还是相对简单的,但假如高度不确定呢?
或者换另外一个角度,当我们的滚动不是纵向的时候,而是横向,该如何处理呢?
useWebSocket
文档地址
详细代码
主要原理是基于 WebSocket,进行一些逻辑封装,比如错误重试逻辑,组件清除后自动 disconnect 等。
可以先看下类型定义,我们可以直观知道 WebSocket 的状态有四种,见 ReadyState:
另外可以看到 typescript 内部有对 WebSocket 回调事件(WebSocketEventMap)以及其实例(WebSocket)的定义。
看入参、状态声明以及返回:
再来看 webSocket 的连接,其中会对相关的回调事件进行处理,比如错误、成功回调。
上面当 WebSocket 错误的时候,也就是 onerror 回调中,我们会调用 reconnect 进行重试。重试支持重试次数的定义,我们来看下 reconnect 函数:
封装暴露常见操作,比如发送信息,断开,连接等方法:
socketUrl 更新的时候,manual (手动连接)为 false,则自动连接:
组件销毁的时候,则断开:
The text was updated successfully, but these errors were encountered: