はじめに
個人開発で「推し活ダイアリー」アプリのフロントエンドをNext.jsで制作しています。
タイトルの通りで、消えたと言ってもテストデータなので実害はなかったのですが、
色々なパターンで作り込んでいたデータなので、ちょっとショックでした...。
教訓と記憶も兼ねて、記事化して残すことにしました。
起きたこと
アカウントを削除用のパスワード入力欄に「目」のアイコンを追加し、クリックすると表示/非表示が切り替わるUIの実装中(以下の画像です)。
パスワードを適当に入力して、「目」をクリックして試していたところ、
mode: "onSubmit"にもかかわらず、バリデーションエラーが出るので、違和感がありましたが...。
正しいパスワードを入力して、「目」をクリックすると、削除ボタンは押していないのにアカウントが削除されました。アカウントに紐づくデータも全部削除されることに...。
原因
<button>にtype属性が設定されていなかったため、デフォルトで type="submit" になり、フォームデータが送信され、アカウントが削除された。
フォームのコード
<AlertDialog open={alertOpen} onOpenChange={setAlertOpen}>
<AlertDialogContent size="sm">
<AlertDialogHeader>
<AlertDialogTitle className="font-semibold">
❗️本当にアカウントを削除しますか?
</AlertDialogTitle>
<AlertDialogDescription>
アカウントを完全に削除するには、パスワードを入力してください。
</AlertDialogDescription>
</AlertDialogHeader>
<form id="form-delete-account" onSubmit={form.handleSubmit(onSubmit)}>
{form.formState.errors.root && (
<p className="mb-4 rounded-md bg-red-50 px-4 py-2 text-sm text-red-600">
{form.formState.errors.root.message}
</p>
)}
<FieldGroup>
<Controller
name="password"
control={form.control}
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<InputGroup>
<InputGroupInput
{...field}
type={showPassword ? "text" : "password"}
aria-invalid={fieldState.invalid}
placeholder="パスワード..."
autoComplete="current-password"
/>
<InputGroupAddon align="inline-end">
<button
type="button" // !ここが漏れていた
onClick={() => setShowPassword(!showPassword)}
className="text-muted-foreground/80"
>
{showPassword ? <Eye /> : <EyeOff />}
</button>
</InputGroupAddon>
</InputGroup>
{fieldState.invalid && (
<FieldError
errors={[fieldState.error]}
className="text-xs"
/>
)}
</Field>
)}
/>
</FieldGroup>
</form>
<AlertDialogFooter>
<AlertDialogCancel>キャンセル</AlertDialogCancel>
<AlertDialogAction
variant="destructive"
type="button"
onClick={(e) => {
e.preventDefault();
form.handleSubmit(onSubmit)();
}}
>
アカウント削除
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
上記フォームは、shadcn/ui + React Hook Form + Zodを使っています。
<button>はtype属性を省略するとデフォルトでtype="submit"になってしまうので、意図せずフォームが送信されてしまうことがあります。
これから、<button>には、必ずtype属性を設定するようにします。