fix(ui): prevent-duplicate-clicks not work

This commit is contained in:
ReaJason
2026-02-08 20:44:00 +08:00
parent 841909fa64
commit d1782309c1
2 changed files with 55 additions and 39 deletions
+18 -9
View File
@@ -1,7 +1,7 @@
import { useQuery } from "@tanstack/react-query";
import { HomeLayout } from "fumadocs-ui/layouts/home";
import { LoaderCircle, WandSparklesIcon } from "lucide-react";
import { useCallback, useState, useTransition } from "react";
import { useCallback, useRef, useState } from "react";
import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";
@@ -89,7 +89,7 @@ export default function MemShellPage() {
});
const { t } = useTranslation(["common", "memshell"]);
const form = useForm({
const form = useForm<MemShellFormSchema>({
resolver: useYupValidationResolver(memShellFormSchema, t),
defaultValues,
});
@@ -100,7 +100,7 @@ export default function MemShellPage() {
>();
const [generateResult, setGenerateResult] = useState<MemShellResult>();
const [packMethod, setPackMethod] = useState<string>("");
const [isActionPending, startTransition] = useTransition();
const submitLockRef = useRef(false);
const submitMemShell = useCallback(
async (data: MemShellFormSchema) => {
@@ -134,10 +134,15 @@ export default function MemShellPage() {
);
const onSubmit = useCallback(
(data: MemShellFormSchema) => {
startTransition(() => {
void submitMemShell(data);
});
async (data: MemShellFormSchema) => {
if (submitLockRef.current) return;
submitLockRef.current = true;
try {
await submitMemShell(data);
} finally {
submitLockRef.current = false;
}
},
[submitMemShell],
);
@@ -156,8 +161,12 @@ export default function MemShellPage() {
form={form}
/>
<PackageConfigCard packerConfig={packerConfig} form={form} />
<Button className="w-full" type="submit" disabled={isActionPending}>
{isActionPending ? (
<Button
className="w-full"
type="submit"
disabled={form.formState.isSubmitting}
>
{form.formState.isSubmitting ? (
<LoaderCircle className="animate-spin" />
) : (
<WandSparklesIcon />
+37 -30
View File
@@ -1,7 +1,7 @@
import { useQuery } from "@tanstack/react-query";
import { HomeLayout } from "fumadocs-ui/layouts/home";
import { LoaderCircle, WandSparklesIcon } from "lucide-react";
import { useState, useTransition } from "react";
import { useRef, useState } from "react";
import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";
@@ -68,38 +68,41 @@ export default function ProbeShellGenerator() {
>();
const [generateResult, setGenerateResult] = useState<ProbeShellResult>();
const [packMethod, setPackMethod] = useState<string>("");
const [isActionPending, startTransition] = useTransition();
const submitLockRef = useRef(false);
const onSubmit = async (data: ProbeShellFormSchema) => {
startTransition(async () => {
try {
const postData = transformToProbePostData(data);
const response = await fetch(`${env.API_URL}/api/probe/generate`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(postData),
});
if (submitLockRef.current) return;
submitLockRef.current = true;
if (!response.ok) {
const json: APIErrorResponse = await response.json();
toast.error(t("toast.generateError", { error: json.error }));
return;
}
try {
const postData = transformToProbePostData(data);
const response = await fetch(`${env.API_URL}/api/probe/generate`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(postData),
});
const result = (await response.json()) as ProbeShellGenerateResponse;
setGenerateResult(result.probeShellResult);
setPackResult(result.packResult);
setAllPackResults(result.allPackResults);
setPackMethod(data.packingMethod);
toast.success(t("toast.generateSuccess"));
} catch (error) {
toast.error(
t("toast.generateError", { error: (error as Error).message }),
);
if (!response.ok) {
const json: APIErrorResponse = await response.json();
toast.error(t("toast.generateError", { error: json.error }));
return;
}
});
const result = (await response.json()) as ProbeShellGenerateResponse;
setGenerateResult(result.probeShellResult);
setPackResult(result.packResult);
setAllPackResults(result.allPackResults);
setPackMethod(data.packingMethod);
toast.success(t("toast.generateSuccess"));
} catch (error) {
toast.error(
t("toast.generateError", { error: (error as Error).message }),
);
} finally {
submitLockRef.current = false;
}
};
return (
<HomeLayout {...baseOptions()} links={siteConfig.navLinks}>
@@ -111,8 +114,12 @@ export default function ProbeShellGenerator() {
<div className="w-full xl:w-1/2 flex flex-col gap-2">
<MainConfigCard form={form} servers={serverConfig} />
<PackageConfigCard form={form} packerConfig={packerConfig} />
<Button className="w-full" type="submit" disabled={isActionPending}>
{isActionPending ? (
<Button
className="w-full"
type="submit"
disabled={form.formState.isSubmitting}
>
{form.formState.isSubmitting ? (
<LoaderCircle className="animate-spin" />
) : (
<WandSparklesIcon />