如何利用ahooks解决多次重复提交问题
Admin 2022-08-10 群英技术资讯 430 次浏览
试想一下,有这么一个场景,有一个表单,你可能多次提交,就很可能导致结果不正确。
解决这类问题的方法有很多,比如添加 loading,在第一次点击之后就无法再次点击。另外一种方法就是给请求异步函数添加上一个静态锁,防止并发产生。这就是 ahooks 的 useLockFn 做的事情。
useLockFn
用于给一个异步函数增加竞态锁,防止并发执行。
它的源码比较简单,如下所示:
import { useRef, useCallback } from 'react'; // 用于给一个异步函数增加竞态锁,防止并发执行。 function useLockFn<P extends any[] = any[], V extends any = any>(fn: (...args: P) => Promise<V>) { // 是否现在处于一个锁中 const lockRef = useRef(false); // 返回的是增加了竞态锁的函数 return useCallback( async (...args: P) => { // 判断请求是否正在进行 if (lockRef.current) return; // 请求中 lockRef.current = true; try { // 执行原有请求 const ret = await fn(...args); // 请求完成,状态锁设置为 false lockRef.current = false; return ret; } catch (e) { // 请求失败,状态锁设置为 false lockRef.current = false; throw e; } }, [fn], ); } export default useLockFn;
可以看到,它的入参是异步函数,返回的是一个增加了竞态锁的函数。通过 lockRef 做一个标识位,初始化的时候它的值为 false。当正在请求,则设置为 true,从而下次再调用这个函数的时候,就直接 return,不执行原函数,从而达到加锁的目的。
虽然实用,但缺点很明显,我需要给每一个需要添加竞态锁的请求异步函数都手动加一遍。那有没有比较通用和方便的方法呢?
答案是可以通过 axios 自动取消重复请求。
对于原生的 XMLHttpRequest 对象发起的 HTTP 请求,可以调用 XMLHttpRequest 对象的 abort 方法。
那么我们项目中常用的 axios 呢?它其实底层也是用的 XMLHttpRequest 对象,它对外暴露取消请求的 API 是 CancelToken。可以使用如下:
const CancelToken = axios.CancelToken; const source = CancelToken.source(); axios.post('/user/12345', { name: 'gopal' }, { cancelToken: source.token }) source.cancel('Operation canceled by the user.'); // 取消请求,参数是可选的
另外一种使用的方法是调用 CancelToken 的构造函数来创建 CancelToken,具体使用如下:
const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { cancel = c; }) }); cancel(); // 取消请求
知道了如何取消请求,那怎么做到自动取消呢?答案是通过 axios 的拦截器。
具体的做法如下:
第一步,定义几个重要的辅助函数。
generateReqKey
:用于根据当前请求的信息,生成请求 Key。只有 key 相同才会判定为是重复请求。这一点很重要,而且可能跟具体的业务场景有关,比如有一种请求,输入框模糊搜索,用户高频输入关键字,一次性发出多个请求,可能先发出的请求,最后才响应,导致实际搜索结果与预期不符。这种其实就只需要根据 URL 和请求方法判定其为重复请求,然后取消之前的请求就可以了。这里我认为,如果有需要的话,可以暴露一个 API 给开发者进行自定义重复的规则。这里我们先根据请求方法、url、以及参数生成唯一的 key 去做。
function generateReqKey(config) { const { method, url, params, data } = config; return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&"); }
const pendingRequest = new Map(); function addPendingRequest(config) { const requestKey = generateReqKey(config); config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => { if (!pendingRequest.has(requestKey)) { pendingRequest.set(requestKey, cancel); } }); }
function removePendingRequest(config) { const requestKey = generateReqKey(config); if (pendingRequest.has(requestKey)) { const cancelToken = pendingRequest.get(requestKey); cancelToken(requestKey); pendingRequest.delete(requestKey); } }
第二步,添加请求拦截器。
axios.interceptors.request.use( function (config) { removePendingRequest(config); // 检查是否存在重复请求,若存在则取消已发的请求 addPendingRequest(config); // 把当前请求信息添加到pendingRequest对象中 return config; }, (error) => { return Promise.reject(error); } );
第二步,添加响应拦截器。
axios.interceptors.response.use( (response) => { removePendingRequest(response.config); // 从pendingRequest对象中移除请求 return response; }, (error) => { removePendingRequest(error.config || {}); // 从pendingRequest对象中移除请求 if (axios.isCancel(error)) { console.log("已取消的重复请求:" + error.message); } else { // 添加异常处理 } return Promise.reject(error); } );
到这一步,我们就通过 axios 完成了自动取消重复请求的功能。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
JavaScript中用set怎样实现数组去重?JavaScript实现数组去重的方法有很多,这里我们主要介绍的用set进行数组去重的方法,文中的示例代码介绍得很详细,有需要的朋友可以参考,接下来就跟随小编一起了解看看吧。
JS对象创建方式有几种,优缺点是什么?在JS开发中,如果要输入姓名、性别、年龄等信息,使用值类型存储,则必须声明多个变量,如果你声明更多的变量,就会造成变量污染,别着急,下面是JS中几种对象创建方法的优缺点比较,希望能帮到对此有需要的你。
这篇文章主要为大家详细介绍了Vue dialog模态框的封装方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
本篇文章给大家带来了关于javascript的相关知识,其中主要整理了编译原理的相关问题,Javascript是一种由Netscape(网景)的LiveScript发展而来的原型化继承的面向对象的动态类型的区分大小写的客户端脚本语言,下面一起来看一下,希望对大家有帮助。
这篇文章给大家总结下JavaScript数组去重的几种方法,面试中也经常会遇到这个问题。文中给大家引申的还有合并数组并去重的方法,感兴趣的朋友跟随脚本之家小编一起学习吧
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008