css
.btn {
pointer-events: all;
animation: btnAnim 4s step-end forwards;
}
.btn:active {
animation: none
}
@keyframes btnAnim {
from {
pointer-events: none;
}
to {
pointer-events: all;
}
}
优点:比较简洁
弊端:没法精准控制接口返回时间,可能有误差,只针对有点击事件的
useLockFn
function useLockFnSelf<P extends any[] = any[], V = 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);
lockRef.current = false;
return ret;
} catch (e) {
lockRef.current = false;
throw e;
}
},
[fn],
);
}
useLockFn 接收一个函数 fn,它接收一个异步函数 fn 作为参数,并返回一个带有锁定状态的函数。这个函数在执行异步操作时,会确保在上一个操作执行完成之前不会被重复调用。这样可以避免出现并发执行的问题。
umi-request全局处理
const set = new Set();
requestInterceptors: [
(url, options) => {
// 在发送请求之前做一些处理
const { method, data, ...restOptions } = options;
//请求方法+链接+参数的字符串
const requestId = `${method}-${url}-${JSON.stringify(data)||''}`;
if (set.has(key)) {
// 如果请求已经存在,则直接返回一个 reject 状态的 Promise,能拦截掉请求
return Promise.reject();
}
requestMap[requestId] = true;
return {
url,
options: {
method,
data,
...restOptions,
},
};
},
],
responseInterceptors: [
(response, options) => {
// 在接收到响应后做一些处理
const { method, url, data } = options;
const requestId = `${method.toLowerCase()}-${url}-${JSON.stringify(data)||''}`;
set.delete(key)
return response;
},
],
-
request的options,method是大写和response 中的method拿到的是小写
-
检测到重复的,写Promise.resolve(),还是会发起请求
-
JSON.stringify(data)可能有坑,建议用更稳妥的组件库
function Test() {
const handleClick = async () => {
const a = await fnA();
const b = await fnB();
}
return <button onClick={handleClick}>接口请求</button>
}
预期的是对整个handleClick做防重,在fnA, fnB 接口都完成前,都不再发送请求。但请求库只能支持对fnA和fnB做单独的防重 useLockFn就能做到
封一个button组件
import React, { useState } from 'react';
import { Button } from 'antd';
export default function LockButton(props) {
const [loading, setLoading] = useState(false);
const onClick = async (e) => {
if (typeof props.onClick === 'function') {
setLoading(true);
try {
await props.onClick(e);
}
finally {
setLoading(false);
}
}
};
return (
<Button
{...props}
loading={loading || props.loading}
disabled={loading || props.disabled}
onClick={onClick}
/>
);
}
babel插件
babel-plugin-async-lock
// 源代码
const onClick = async () => {
console.log('async function')
await fnA();
}
// 经babel插件处理后的代码
function _lock$(fn) {
let locking = false;
const _lockedFn$ = async function (...args) {
if (locking) {
return;
}
try {
locking = true;
const r = await fn.apply(this, args);
return r;
} catch (e) {
throw e;
} finally {
locking = false;
}
};
return _lockedFn$;
}
const onClick = _lock$(async () => {
console.log('async function')
await fnA();
});