プログラミングスキルアップ|Next.jsを学ぶ#03

Youtube動画を見ながらNext.jsの学習を進めようの記事、第三弾となります。

1回目の記事はこちら

2回目の記事はこちら

今回の対象ファイル

今回の3回目ではログインページのフォーム追加であったり、サインアップ・ログインボタンの実装をしていってます。

主に編集を加えていってるのは以下のファイル

src/app/page.tsx
src/components/forms/PatientForm.tsx
src/components/CustomFormField.tsx
src/components/SubmitButton.tsx

これに新しく、validation.tsを作成しています。

src/lib/validation.ts

コンポーネント部分の調整と、フォーム入力の際のバリデーションに関する実装、という感じですね。

フックの利用

PatientForm.tsxの編集では、フックと呼ばれるものが出てきました。

いまいち理解していなかったので、例のごとくChatGPTに質問。

useStateは状態管理に関するもので、useRouterはルーティング機能に関するものなんですね。

現在の情報、状態の更新、状態の初期値を指定することで、レンダリングするたびに更新されるというのがuseStateだと。

そしてuseRouterは

useRouter は Next.js のフックで、ページのルーティング(URL の遷移やクエリパラメータの取得など)に関する情報やメソッドにアクセスするために使用されます。Next.js はページベースのルーティングシステムを提供しており、useRouter はこのシステムと連携するための強力なツールです。

と書かれていますので、オブジェクトを作った上で以下メソッドを利用してページ遷移の実装をしているんですね。

この辺はWordPress(PHP)での扱いに近いなぁと思いながらコーディングを進めていきます。

フォームフィールドに関するコンポーネントの編集

フォーム入力部分については、テキスト形式(名前など)なのか、数字形式(電話番号)なのかの条件分けが必要になるので、今回はswitch文を利用して作成していきます。

const RenderField = ({field, props}: {field: any; props:CustomProps}) => {
  const {fieldType, iconSrc, iconAlt, placeholder} = props;

  switch (fieldType){
    case FormFieldType.INPUT:
      return(
        <div className="flex rounded-md border border-dark-500 bg-dark-400">
          {iconSrc && (
            <Image
              src = {iconSrc}
              height = {24}
              width= {24}
              alt = {iconAlt || "icon"}
              className = "ml-2"
            />
          )}
          <FormControl>
            <Input 
              placeholder={placeholder}
              {...field}
              className="shad-input border-0"
            />
          </FormControl>
        </div>
      )
    case FormFieldType.PHONE_INPUT:
      return(
        <FormControl>
          <PhoneInput 
            defaultCountry="JP"
            placeholder = {placeholder}
            international
            withCountryCallingCode
            value = {field.value as E164Number | undefined}
            onChange={field.onChange}
            className="input-phone"
          />
        </FormControl>
      )
    default:
      break;
  }
}

ここで太字にしているPhoneInputというのがnpmでインストールする必要がありました。

React-phone-number-inputというものが用意されているようで、これをnpmで実行すれば使えるようになりました。

属性値には国名を入れることになりますが、日本の場合は『JP』と入力すればOK。

これを入れることで、入力フォームによくある国旗と+●●みたいなのが出てくるようになりました。

実際のフォームフィールドについて項目を追加

今回は、氏名・メールアドレス・電話番号が必要になるフォームの実装でしたので、コードはこんな感じになっています。

<Form {...form}>
    <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6 flex-1">
      <section className="mb-12 space-y-4">
        <h1 className=" header ">Hi there ✊</h1>
        <p className="text-dark-700">schedule your first appointment.</p>
      </section>

      <CustomFormField 
        fieldType = {FormFieldType.INPUT}
        control = {form.control}
        name="name"
        label="Full name"
        placeholder = "サンプル太郎"
        iconSrc = "/assets/icons/user.svg"
        iconAlt = "user"
      />

      <CustomFormField 
        fieldType = {FormFieldType.INPUT}
        control = {form.control}
        name="email"
        label="Email"
        placeholder = "sampletaro@test.com"
        iconSrc = "/assets/icons/email.svg"
        iconAlt = "email"
      />
      
      <CustomFormField 
        fieldType = {FormFieldType.PHONE_INPUT}
        control = {form.control}
        name="phone"
        label="Phone number"
        placeholder = "01234567890"
        iconSrc = "/assets/icons/phone.svg"
        iconAlt = "phone"
      />

このCustomFormFieldひとつひとつが入力欄になっているわけですね。

そしてSubmitボタンを作成していくことになります。

Validationの実装

バリデーションはzodを利用して簡潔に書いていきます。

これ、すごく便利ですね。フォームのバリデーションって結構めんどいイメージでしたけど、zod利用するとかなり簡潔に記載することができました。

export const UserFormValidation = z.object({
  username: z.string()
  .min(2, "ユーザー名は2文字以上入力してください")
  .max(50, "ユーザー名は50文字以内で入力してください"),
  email: z.string().email("無効なアドレスです"),
  phone: z.string().refine((phone) => /^\+\d{1,14}$/.test(phone), "無効な電話番号です")
})

動画ではエラーメッセージも英語で書いてますが、ここは自分の学習目的なので日本語で置き換えてます。

まとめ

こんな感じで、やっとこ動画の進捗が1時間を超えてきました(実際は5時間以上ある)

今のところはこんな感じ。左側のフォームが増えているのがわかります。

さぁ引き続き頑張っていきます!

この記事を書いた人

kumarishin