如何真正优雅解决按钮”重复点击“问题?
共 2452字,需浏览 5分钟
·
2020-12-06 12:34
关注公众号 前端人,回复“加群”
添加无广告优质学习群
一、对思否的一点吐槽
思否上的许多按钮都没有做重复点击检测的问题,往往导致点击没反应,多次点击后突然发表多条相同内容,比如和一位同学的私信:
又如一个问题下的评论:
如果你在这篇文章下发表评论,可以不小心多点几下"提交评论"按钮,会发现也存在相同的问题~~~
这个问题怎么解决呢?
简单点,使用一个lock标记,在请求发出时上锁,上锁后就不可以再发请求,可以在请求结束后解锁:
let clickButton = (function () {
let lock = false
return function (postParams) {
if (lock) return
lock = true
// 假设使用axios发送请求
axios.post('urlxxx', postParams).then(
// 表单提交成功
).catch(error => {
// 表单提交出错
console.log(error)
}).finally(() => {
// 不管成功失败 都解锁
lock = false
})
}
})()
button.addEventListener('click', clickButton)
当然对于button按钮,可以使用setAttribute('disabled', xxx)
和removeAttribute('disabled')
来代替lock标记。
这个方案问题在于,对于每一次按钮点击,我们都要写个lock标记,相当于重复的逻辑会出现在代码的各个地方——是不是可以封装一下呢?
二、封装按钮锁定、解锁逻辑
写一个装饰器将逻辑封装起来:
function ignoreMultiClick(func, manual = false) {
let lock = false
return function (...args) {
if (lock) return
lock = true
let done = () => (lock = false)
if (manual) return func.call(this, ...args, done)
let promise = func.call(this, ...args)
Promise.resolve(promise).finally(done)
return promise
}
}
将想监听点击回调函数func作为传递给ignoreMultiClick进行装饰,会返回一个新的函数,使用该函数作为点击的回调事件即可。
有两种方法解锁:
手动解锁:
可以给ignoreMultiClick传递一个参数manual,意思是主动调用解锁。
若该参数为truthy,则点击事件触发时会给原始的点击回调func传递一个参数done,done是一个函数,调用它可以解锁。
自动解锁:
可以使原监听函数func返回一个promise,在该promise决议后自动执行解锁操作。
因为Promise管理回调函数非常方便,并且像axios这样非常常用的请求库返回值本身也是一个promise,所以默认情况使用这种方式。
当然返回promise并不是必须的,有时候我们在发请求前会进行一些验证,验证没通过则直接return,此时装饰器函数也能正常处理,因为使用Promise.resolve包裹了一下promise:Promise.resolve(promise).finally(done)。
三、使用实例
自动解锁使用例子:
let clickButton = ignoreMultiClick(function (postParams) {
// 假设有一些检测表单的操作,检查不通过则直接返回
if (!checkForm()){
return done()
}
// 返回promise
return axios.post('urlxxx', postParams).then(
// 表单提交成功
).catch(error => {
// 表单提交出错
console.log(error)
})
})
button.addEventListener('click', clickButton)
手动解锁:
let clickButton = ignoreMultiClick(function (postParams, done) {
// 表单验证不通过解锁
if (!checkForm()){
return done()
}
axios.post('urlxxx', postParams).then(
// 表单提交成功
).catch(error => {
// 表单提交出错
console.log(error)
}).finally(() => done()) // 请求结束解锁
})
button.addEventListener('click', clickButton)
普通场景下还是自动解锁比较简单,因为可能有多个条件分支,手动解锁需要在每一个返回的地方都调用done。
原文:segmentfault.com/a/1190000038291773
1.你的点赞、在看 是对【鬼哥】最大的支持,一键三连吧
2.关注公众号前端人,回复资料包
领取我整理的前端进阶资料包
3.回复加群
,加入前端进阶群,和小伙伴一起学习讨论!