iT邦幫忙

2024 iThome 鐵人賽

DAY 7
0
Modern Web

前進React 生態系 : 技術應用與概念解析系列 第 7

Day 07 - 結合 Zod 與 React Hook Form 實現簡潔的表單管理

  • 分享至 

  • xImage
  •  

為什麼需要使用 React Hook Form?

在沒有使用的表單管理套件的情況下,需要定義許多狀態來處理表單邏輯和各種錯誤狀況。而使用像 React Hook Form 這類的套件,可以減少程式碼量,並提供更多功能,使表單處理更加簡單快速。

而使用 Zod 是因為我們希望用戶在表單填入的資料是符合預期的,用 Zod 可以更簡單的定義。

實做範例

在開始之前,需要先安裝以下套件:

  • react-hook-form
  • zod
  • @hookform/resolvers(為了將 Zod 與 React Hook Form 結合使用)

範例程式碼

import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";

const userSchema = z.object({
  name: z.string(),
  email: z.string().email({ message: "Email is required" }),
  age: z
    .number({
      required_error: "Age is required",
      invalid_type_error: "Age must be a number",
    })
    .int({ message: "Age must be an integer" })
    .refine((age) => age >= 18 && age <= 60, {
      message: "Age must be between 18 and 60",
    }),
  description: z
    .string()
    .max(100, { message: "Description is too long" })
    .optional(),
});

type User = z.infer<typeof userSchema>;

export default function Form() {
  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting },
  } = useForm<User>({
    resolver: zodResolver(userSchema),
    defaultValues: {
      name: "Unknown User",
    },
  });

  const onSubmit = (values: User) => {
    //只有型別驗證通過,才會執行
    console.log(values);
    /*
      {
        "name": "John",
        "email": "test@test.com",
        "age": 25,
      }
      */
    // ... 處理表單邏輯
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label htmlFor="name">Name : </label>
      <input id="name" type="text" {...register("name")} />
      {errors.name && <p>{errors.name.message}</p>}

      <label htmlFor="email">Email : </label>
      <input id="email" type="email" {...register("email")} />
      {errors.email && <p>{errors.email.message}</p>}

      <label htmlFor="age">Age : </label>
      <input
        id="age"
        type="number"
        {...register("age", { valueAsNumber: true })}
      />
      {errors.age && <p>{errors.age.message}</p>}

      <label htmlFor="description">Description : </label>
      <textarea id="description" type="text" {...register("description")} />
      {errors.description && <p>{errors.description.message}</p>}

      <button disabled={isSubmitting} type="submit">
        {isSubmitting ? "Submitting..." : "Submit"}
      </button>
    </form>
  );
}

定義 Schema

Zod 除了一般常見的型別外,也可以針對一些特殊的型別做驗證,像是 email 格式或其他自訂的驗證規則。

以下是範例的欄位說明:

  • name:必填的字串。
  • email:必須是有效的 email 格式。
  • age:使用 refine 來自訂驗證規則,確保年齡必須是整數且介於 18 到 60 歲之間。
  • description:最多 100 個字元,是可選的。

驗證規則都可以附加 message 參數,用來定義自訂的錯誤訊息。required_errorinvalid_type_error 參數則是用來定義必填欄位和無效型別的錯誤訊息。
針對非必填欄位,也可以使用 optional 來設定是否必填。

使用 useForm 處理表單

  • register:透過 register 方法來連接 input 欄位與 React Hook Form,使得它能夠追蹤這些欄位的值與錯誤狀態。
  • handleSubmit:當表單提交時,會根據定義的驗證規則來判斷表單是否有效,並接收傳入的表單資料。
  • formState: { errors, isSubmitting }:這邊包含表單的各種狀態,errors 用來取得各個欄位的錯誤訊息,isSubmitting 則是用來表示表單是否正在提交中。
  • resolver: zodResolver(userSchema):將 Zod 的 schema 傳入作為表單的驗證邏輯。
  • defaultValues: 用來設定表單初始值,例如範例中的 name 預設為 "Unknown User"

實際樣式

  • handleSubmit(onSubmit): 若表單通過驗證,才會執行 onSubmit 函式。
  • {...register("欄位名稱")}:透過 register 方法將 input 欄位與 React Hook Form 連接,實現對欄位值和錯誤狀態的追蹤。
  • valueAsNumber:會在型別驗證前先把 string 轉成 number。
  • 錯誤訊息顯示:如果表單欄位有錯誤,會在對應的欄位下方顯示錯誤訊息。例如對年齡欄位,可以使用 {errors.age && <p>{errors.age.message}</p>} 來動態顯示相應的錯誤資訊。

其他更多的說明可以參考 React Hook FormZod 的文件。


上一篇
Day 06 - TypeScript 語法技巧與 Zod 簡介
下一篇
Day 08 - 掌握 React 中的 Ref:useRef、Callback Ref 與 React 19 新特性介紹
系列文
前進React 生態系 : 技術應用與概念解析30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言