1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

<button>のtype属性が入っておらず、データが全部消えました...。

1
Posted at

はじめに

個人開発で「推し活ダイアリー」アプリのフロントエンドを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属性を設定するようにします。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?