iT邦幫忙

2022 iThome 鐵人賽

DAY 15
0
自我挑戰組

新手前端與真實世界的開發 feat.React 與他的夥伴系列 第 15

Day-15 專案演練 - 打造表單功能 react-hook-form

  • 分享至 

  • xImage
  •  

Day-15 專案演練 - 打造表單功能 react-hook-form

表單在 react 中的處理相當的繁複,每個 input 都要一個 onCharge,還要一個一個上,我剛學如何處理的時候也吃了不少苦頭,直到我遇見 react-hook-form,今天就來試試這個套件如何幫助我們處理表單。

新增 addTodo 表單 UI

因為要做新增 todo,所以我們肯定就需要一個表單來輸入資料了,在昨天的進度當中我們已經做好的一個跳窗裡面能夠新增 todo 的 UI :

<form> 包住全部的 <input>

使用 <form> 來包所有的 <input>,也包含送出表單的按鈕。

以下是 MDN 的範例,點進去網頁就能夠自己動手玩一玩。

<form action="" method="get" class="form-example">
  <div class="form-example">
    <label for="name">Enter your name: </label>
    <input type="text" name="name" id="name" required />
  </div>
  <div class="form-example">
    <label for="email">Enter your email: </label>
    <input type="email" name="email" id="email" required />
  </div>
  <div class="form-example">
    <input type="submit" value="Subscribe!" />
  </div>
</form>

觀察這一組結構,其實會發現幾件事情 :

  • 所有的 input 都在 form 裡面
  • 每個 label 的 for 屬性都跟它的 input 有對應

表單的 <label><input>的 id

<label><input> 的 id 對應上的時候,我們就不需要真的點選到 <input>,只要點到 <label> 的時候,它的 <input> 就會進入 focus 狀態,這時候直接輸入就行了,相當方便。

注意到這個細節,並且確實的使用在自己的網頁上,會大大提升使用者在使用表單的體驗,多多利用吧。

安裝 react-hook-form 與基本使用方式

讓我們來造訪一下 react-hook-form 的官方網站吧!

安裝一下 :

npm install react-hook-form

觀察一下官方的範例 :

import { useForm } from "react-hook-form";

export default function App() {
  const {
    register,
    handleSubmit,
    watch,
    formState: { errors },
  } = useForm();
  const onSubmit = (data) => console.log(data);

  console.log(watch("example")); // 監聽 input name example

  return (
    /* "handleSubmit" will validate your inputs before invoking "onSubmit" */
    <form onSubmit={handleSubmit(onSubmit)}>
      {/* register your input into the hook by invoking the "register" function */}
      <input defaultValue="test" {...register("example")} />

      {/* include validation with required or other standard HTML validation rules */}
      <input {...register("exampleRequired", { required: true })} />
      {/* errors will return when field validation fails  */}
      {errors.exampleRequired && <span>This field is required</span>}

      <input type="submit" />
    </form>
  );
}

我們會發現,要先使用 useForm() Hook,裡面提供了一些方法 :

  • register 在 input 中綁定,讓 onSubmit 時可以取得個別 input 資料。
  • handleSubmit 顧名思義就是處理表單送出 (submit) 時需要做的事情。

在專案中使用 react-hook-form

  1. 在我們需要表單的地方引入 react-hook-form,也就是 TodoPage 裡面,接下來使用 useForm 安裝功能近來

TodoPage.tsx

import { useForm } from "react-hook-form";

const TodoPage: React.FC = () => {
  ...
   const {
    register,
    handleSubmit,
    watch,
    reset,
    formState: { errors },
  } = useForm();
  ...
}
  1. 在表單中安裝 registerhandleSubmit
...
<form onSubmit={handleSubmit(onSubmit)}>
  <div className="grid grid-cols-1 gap-[8px] w-[340px]">
    <label className="block ">
      <span className="text-gray-700">標題</span>
      <input
        type="text"
        className="
                    mt-1
                    block
                    w-full
                    rounded-md
                    bg-gray-100
                    border-transparent
                    focus:border-gray-500 focus:bg-white focus:ring-0
                  "
        {...register("title", { required: true })}
      />
    </label>

    <label className="block">
      <span className="text-gray-700">時間</span>
      <input
        type="date"
        className="
                    mt-1
                    block
                    w-full
                    rounded-md
                    bg-gray-100
                    border-transparent
                    focus:border-gray-500 focus:bg-white focus:ring-0
                  "
        {...register("time")}
        value="2018-07-22"
      />
    </label>
    <label className="block">
      <span className="text-gray-700">詳情描述</span>
      <textarea
        className="
                    mt-1
                    block
                    w-full
                    rounded-md
                    bg-gray-100
                    border-transparent
                    focus:border-gray-500 focus:bg-white focus:ring-0
                  "
        rows={3}
        {...register("info")}
      />
    </label>
    <label className="flex justify-center items-center">
      <input
        type="submit"
        className="
                    mt-1
                    block
                    w-full py-[4px]
                    cursor-pointer
                    rounded-md
                    bg-gray-300
                    border-transparent
                    focus:border-gray-500 focus:bg-white focus:ring-0
                  "
      />
    </label>
  </div>
</form>
...
  1. 撰寫 onSubmit 事件,先試試看能不能在送出表單時,夠取得資料:
...
  const onSubmit = (data) => console.log(data);
...

將畫面與 state 綁定

能夠拿到表單資料之後,可以來撰寫增加 todo 功能了!

首先,我們需要一個 state,是用來裝清單的內容,也就是要繞進表格裡的資料,長相如下 :

const [list, setList] =
  useState <
  any >
  (() => [
    { time: "2018-07-22", title: "購物", info: "日用品", checked: false },
    { time: "2022-09-15", title: "鐵人賽", info: "day-10", checked: true },
    { time: "2022-09-25", title: "摺棉被", info: "", checked: false },
  ]);

有了 list 之後,我們的畫面要跟 list 的內容有關係,先把內容跟 UI 綁在一起。

在這邊使用的是 JS 的 map 來做遍歷 list,並且把資料放到畫面上 :

...
            <Tbody>
              {list.map((i, key) => (
                <Tr key={key}>
                  <Td>
                    <Checkbox isChecked={i.checked} />
                  </Td>
                  <Td>{i.title}</Td>
                  <Td>{i.info}</Td>
                  <Td className="flex items-center">
                    <span>
                      <svg
                        width="20"
                        height="20"
                        viewBox="0 0 30 30"
                        fill="none"
                        xmlns="http://www.w3.org/2000/svg"
                      >
                        <path
                          d="M3.75 21.825V25.625C3.75 25.975 4.025 26.25 4.375 26.25H8.175C8.3375 26.25 8.5 26.1875 8.6125 26.0625L22.2625 12.425L17.575 7.73748L3.9375 21.375C3.8125 21.5 3.75 21.65 3.75 21.825V21.825ZM25.8875 8.79998C26.375 8.31248 26.375 7.52498 25.8875 7.03748L22.9625 4.11248C22.475 3.62498 21.6875 3.62498 21.2 4.11248L18.9125 6.39998L23.6 11.0875L25.8875 8.79998Z"
                          fill="black"
                        />
                      </svg>
                    </span>
                    <span>
                      <svg
                        width="25"
                        height="25"
                        viewBox="0 0 30 30"
                        fill="none"
                        xmlns="http://www.w3.org/2000/svg"
                      >
                        <path
                          d="M8.39946 24.6985C8.39946 26.0848 9.53373 27.2191 10.9201 27.2191H21.0024C22.3888 27.2191 23.523 26.0848 23.523 24.6985V9.57494H8.39946V24.6985ZM11.4998 15.7252L13.2768 13.9482L15.9612 16.62L18.6331 13.9482L20.4101 15.7252L17.7383 18.397L20.4101 21.0689L18.6331 22.8459L15.9612 20.174L13.2894 22.8459L11.5124 21.0689L14.1842 18.397L11.4998 15.7252ZM20.3723 5.79404L19.112 4.53374H12.8105L11.5502 5.79404H7.13916V8.31464H24.7833V5.79404H20.3723Z"
                          fill="black"
                        />
                      </svg>
                    </span>
                  </Td>
                </Tr>
              ))}
            </Tbody>
            ...

新增 todo 的功能

好,這樣我們的畫面跟 list 這個 state 同步了,這意味著,我們只要增加 list 中的項目,就可以改變畫面了!

我們什麼時候要增加 todo 進去 list 呢?那就是在送出表單的時候,所以回到 onSubmit,我們在裡面做一些事情 :

  1. setList() 改變 list 的內容
  2. 改變之後,記得關掉跳窗。
...
  const onSubmit = (data) => {
    setList([...list, data]); // 更新 list 把表單的 data 放進去
    if (isOpen) setIsOpen(false); // 更新完之後關掉跳窗
    console.log("list", list); // 測試看看資料有沒有被新增
  };
  ...

新增功能做好了,但測試的時候會發現,舊的內容還會被保存在表單裡面,接下來我們就來處理這個狀況吧。

使用 reset() 來清空表單

首先,先從 useFrom() 裡面把 reset() 取出來 :

...
const {
    register,
    handleSubmit,
    watch,
    reset, // 在這裡喔!
    formState: { errors },
  } = useForm();
...

再來就是用,很簡單的 XD

...
  const onSubmit = (data) => {
    setList([...list, data]);
    if (isOpen) setIsOpen(false);
    reset(); // 在這裡喔!
    console.log("list", list);
  };
  ...

repo

附上程式碼

結語

使用 react-hook-form 有效的解決在 react function component 中綁定表單的麻煩,一用成主顧,真的讓表單處理變得比較簡單了。

參考資料


上一篇
Day-14 專案演練 - React 拆元件的思路與操作(下)
下一篇
Day-16 掌控表格的行與列 react-table 基本安裝與使用
系列文
新手前端與真實世界的開發 feat.React 與他的夥伴30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言