Skip to content

Commit 35e2104

Browse files
committed
fix(ui): prevent-duplicate-clicks not work
1 parent 8774579 commit 35e2104

2 files changed

Lines changed: 55 additions & 39 deletions

File tree

web/app/routes/memshell.tsx

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useQuery } from "@tanstack/react-query";
22
import { HomeLayout } from "fumadocs-ui/layouts/home";
33
import { LoaderCircle, WandSparklesIcon } from "lucide-react";
4-
import { useCallback, useState, useTransition } from "react";
4+
import { useCallback, useRef, useState } from "react";
55
import { useForm } from "react-hook-form";
66
import { useTranslation } from "react-i18next";
77
import { toast } from "sonner";
@@ -89,7 +89,7 @@ export default function MemShellPage() {
8989
});
9090

9191
const { t } = useTranslation(["common", "memshell"]);
92-
const form = useForm({
92+
const form = useForm<MemShellFormSchema>({
9393
resolver: useYupValidationResolver(memShellFormSchema, t),
9494
defaultValues,
9595
});
@@ -100,7 +100,7 @@ export default function MemShellPage() {
100100
>();
101101
const [generateResult, setGenerateResult] = useState<MemShellResult>();
102102
const [packMethod, setPackMethod] = useState<string>("");
103-
const [isActionPending, startTransition] = useTransition();
103+
const submitLockRef = useRef(false);
104104

105105
const submitMemShell = useCallback(
106106
async (data: MemShellFormSchema) => {
@@ -134,10 +134,15 @@ export default function MemShellPage() {
134134
);
135135

136136
const onSubmit = useCallback(
137-
(data: MemShellFormSchema) => {
138-
startTransition(() => {
139-
void submitMemShell(data);
140-
});
137+
async (data: MemShellFormSchema) => {
138+
if (submitLockRef.current) return;
139+
submitLockRef.current = true;
140+
141+
try {
142+
await submitMemShell(data);
143+
} finally {
144+
submitLockRef.current = false;
145+
}
141146
},
142147
[submitMemShell],
143148
);
@@ -156,8 +161,12 @@ export default function MemShellPage() {
156161
form={form}
157162
/>
158163
<PackageConfigCard packerConfig={packerConfig} form={form} />
159-
<Button className="w-full" type="submit" disabled={isActionPending}>
160-
{isActionPending ? (
164+
<Button
165+
className="w-full"
166+
type="submit"
167+
disabled={form.formState.isSubmitting}
168+
>
169+
{form.formState.isSubmitting ? (
161170
<LoaderCircle className="animate-spin" />
162171
) : (
163172
<WandSparklesIcon />

web/app/routes/probeshell.tsx

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useQuery } from "@tanstack/react-query";
22
import { HomeLayout } from "fumadocs-ui/layouts/home";
33
import { LoaderCircle, WandSparklesIcon } from "lucide-react";
4-
import { useState, useTransition } from "react";
4+
import { useRef, useState } from "react";
55
import { useForm } from "react-hook-form";
66
import { useTranslation } from "react-i18next";
77
import { toast } from "sonner";
@@ -68,38 +68,41 @@ export default function ProbeShellGenerator() {
6868
>();
6969
const [generateResult, setGenerateResult] = useState<ProbeShellResult>();
7070
const [packMethod, setPackMethod] = useState<string>("");
71-
const [isActionPending, startTransition] = useTransition();
71+
const submitLockRef = useRef(false);
7272

7373
const onSubmit = async (data: ProbeShellFormSchema) => {
74-
startTransition(async () => {
75-
try {
76-
const postData = transformToProbePostData(data);
77-
const response = await fetch(`${env.API_URL}/api/probe/generate`, {
78-
method: "POST",
79-
headers: {
80-
"Content-Type": "application/json",
81-
},
82-
body: JSON.stringify(postData),
83-
});
74+
if (submitLockRef.current) return;
75+
submitLockRef.current = true;
8476

85-
if (!response.ok) {
86-
const json: APIErrorResponse = await response.json();
87-
toast.error(t("toast.generateError", { error: json.error }));
88-
return;
89-
}
77+
try {
78+
const postData = transformToProbePostData(data);
79+
const response = await fetch(`${env.API_URL}/api/probe/generate`, {
80+
method: "POST",
81+
headers: {
82+
"Content-Type": "application/json",
83+
},
84+
body: JSON.stringify(postData),
85+
});
9086

91-
const result = (await response.json()) as ProbeShellGenerateResponse;
92-
setGenerateResult(result.probeShellResult);
93-
setPackResult(result.packResult);
94-
setAllPackResults(result.allPackResults);
95-
setPackMethod(data.packingMethod);
96-
toast.success(t("toast.generateSuccess"));
97-
} catch (error) {
98-
toast.error(
99-
t("toast.generateError", { error: (error as Error).message }),
100-
);
87+
if (!response.ok) {
88+
const json: APIErrorResponse = await response.json();
89+
toast.error(t("toast.generateError", { error: json.error }));
90+
return;
10191
}
102-
});
92+
93+
const result = (await response.json()) as ProbeShellGenerateResponse;
94+
setGenerateResult(result.probeShellResult);
95+
setPackResult(result.packResult);
96+
setAllPackResults(result.allPackResults);
97+
setPackMethod(data.packingMethod);
98+
toast.success(t("toast.generateSuccess"));
99+
} catch (error) {
100+
toast.error(
101+
t("toast.generateError", { error: (error as Error).message }),
102+
);
103+
} finally {
104+
submitLockRef.current = false;
105+
}
103106
};
104107
return (
105108
<HomeLayout {...baseOptions()} links={siteConfig.navLinks}>
@@ -111,8 +114,12 @@ export default function ProbeShellGenerator() {
111114
<div className="w-full xl:w-1/2 flex flex-col gap-2">
112115
<MainConfigCard form={form} servers={serverConfig} />
113116
<PackageConfigCard form={form} packerConfig={packerConfig} />
114-
<Button className="w-full" type="submit" disabled={isActionPending}>
115-
{isActionPending ? (
117+
<Button
118+
className="w-full"
119+
type="submit"
120+
disabled={form.formState.isSubmitting}
121+
>
122+
{form.formState.isSubmitting ? (
116123
<LoaderCircle className="animate-spin" />
117124
) : (
118125
<WandSparklesIcon />

0 commit comments

Comments
 (0)