5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

RemixとZodを使用したバリデーションのサンプルコードを実装

Posted at

概要

  • Remixを採用してみて、非常に開発体験が良い一方で、Nextjsなどに比べると情報量が少ないと感じるケースが多くあります。
  • バリデーション実装にZodを使用したいというケースは多いと思いますが、RemixでZodを使用するサンプルコードなどはなかなか見つからず、少し苦労したため、他の人にも役立つように、シンプルなサンプルコードを実装しました。

今回はRemixのaction内(サーバー側での処理)での入力値検証を行っています。こうすることで、より安全かつ開発体験良く、バリデーションを実装することが可能です。

サンプルコード

import { json, type ActionFunctionArgs, redirect } from "@remix-run/node";
import { Form, useActionData, useNavigation } from "@remix-run/react";

import { z } from "zod";

const Schema = z.object({
  name: z.string().min(1, { message: "名前は必須です。" }),
  job: z.string().min(1, { message: "職業は必須です。" }),
});

export async function action({ request }: ActionFunctionArgs) {
  // リクエストからフォームデータを取得して、オブジェクトとして扱えるように変換。
  const formDataObject = Object.fromEntries(await request.formData());
  // safeParseを使用し、上記オブジェクトを検証。
  const validationResult = Schema.safeParse(formDataObject);
  if (!validationResult.success)
    return json({
      validationMessages: validationResult.error.flatten().fieldErrors,
    });

  // ユーザー登録処理。今回は主題でないので、中身は割愛
  await createUser(formDataObject);
  return redirect("/users");
}

export default function UsersNew() {
  const actionData = useActionData<typeof action>();
  const validationMessages = actionData?.validationMessages;
  const submitting = useNavigation().state === "submitting";

  {/* スタイリングについては主題ではないので、多少雑な部分があります。*/}
  return (
    <Form
      method="post"
      className="space-y-2 w-96 mx-auto border-2 p-6 rounded-md shadow-sm"
    >
      <div>
        <div className="flex flex-col gap-1">
          <label htmlFor="name" className="font-bold text-sm">
            名前
          </label>
          <input
            type="text"
            id="name"
            name="name"
            className="py-1 px-2 border-2 border-gray-300 focus:outline-none  focus:border-blue-300 rounded-md"
          />
        </div>
        {validationMessages?.name && (
          <p className="text-sm font-bold text-red-500">
            {validationMessages.name[0]}
          </p>
        )}
      </div>

      <div>
        <div className="flex flex-col gap-1">
          <label htmlFor="" className="font-bold text-sm">
            職業
          </label>
          <input
            type="text"
            id="job"
            name="job"
            className="py-1 px-2 border-2 border-gray-300 focus:outline-none  focus:border-blue-300 rounded-md"
          />
        </div>
        {validationMessages?.job && (
          <p className="text-sm font-bold text-red-500">
            {validationMessages.job[0]}
          </p>
        )}
      </div>

      <div className="flex">
        <button
          type="submit"
          className="ml-auto bg-blue-500 rounded-lg py-2 px-4 text-white font-bold hover:bg-blue-500/80"
          disabled={submitting}
        >
          {submitting ? "登録中..." : "登録"}
        </button>
      </div>
    </Form>
  );
}

実装画面

初期表示

image.png

バリデーションエラー(空の状態で登録)

image.png

上記から職業だけ入力

image.png

終わりに

  • 非常に簡素な記事になりましたが、バリデーションはアプリ開発においてほぼ必須かと思いますので、お役に立てると幸いです。
5
3
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
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?