之前提交表单都要写 loading = true、disabled = true、.finally(() => loading = false)?
感觉像是在重复造轮子。
比如有个业务逻辑是:
用户点击“提交订单”
点击“发送验证码”
点击“保存设置”
每次我的写法都是:
定义一个
loading状态;在点击时设为
true;禁用按钮;
发起请求;
成功或失败后,再设回
false。
一段逻辑,复制粘贴十次。
更糟的是——一旦忘记写 .finally,按钮就永远禁用;一旦并发请求没处理好,照样重复提交。
现在直接封装好一个自定义 Hook 每次碰到这种情景直接复制 下面是react的写法:
const [handleSubmit, isSubmitting] = useSubmitLock(async (formData) => {
await api.submitOrder(formData);
message.success('下单成功!');
});
return (
<button disabled={isSubmitting} onClick={() => handleSubmit(data)}>
{isSubmitting ? '提交中...' : '立即下单'}
</button>
);import { useState, useCallback } from 'react';
type AsyncFunction<T extends any[], R> = (...args: T) => Promise<R>;
export const useSubmitLock = <T extends any[], R>(
asyncFn: AsyncFunction<T, R>
) => {
const [isLocked, setIsLocked] = useState(false);
const wrappedFn = useCallback(
async (...args: T): Promise<R | undefined> => {
if (isLocked) {
console.warn('操作正在进行中,请勿重复提交');
return; // 直接拦截,不执行函数
}
setIsLocked(true);
try {
const result = await asyncFn(...args);
return result;
} finally {
setIsLocked(false); // 无论成功失败,一定解锁
}
},
[isLocked, asyncFn]
);
return [wrappedFn, isLocked] as const;
};