Sean's Blog

An image showing avatar

Hi, I'm Sean

這裡記錄我學習網站開發的筆記
歡迎交流 (ゝ∀・)b

LinkedInGitHub

Forms in React

本文介紹如何在 React 中使用 Forms 表單相關元素。

Controlled Components

在 HTML 中,表單 (Forms) 裡面的項目像是 Input、Textarea、Select 會自動跟著使用者輸入的值而改變狀態,而在 React 中,狀態則是透過 setState() 來更新,屬於單向資料流。

因此,當我們在 React 使用 Input 時,就要一起把 State 與 Input 的 value 屬性做綁定,這麼一來 Input 的值又會是透過 React 控制了。此時,這樣的元件又被稱作 Controlled Components,很重要,記得要綁定!

表單範例:Input、Textarea、Checkbox

可以看到 Input, Textarea 的 value 都被我們綁上了 React 的 State,還有 Checkbox 的 checked 屬性也是。

現在這些表單的值都會等於 React 的 State,因此我們透過 onChange 事件去更改 State,並且監聽每一個 Keystroke 隨時更新表單呈現的值。

除此之外,這裡的 handleChange 有幾個小巧思,可以特別留意一下:

  1. 回傳時,我們使用 Spread 來把原物件展開,再添加新的 Key-value Pair
  2. 所有表單元素都有 name 屬性用來聲明欄位名稱,我們使用 ES6 的 Computed Property 來動態取得 name,藉由它來當作新的物件的 Key
  3. 透過三元判斷,去判斷 type 是否為 Checkbox,因為只有 Checkbox 需要的值是 checked (Boolean) 而非 value (String)
1import React from 'react';
2
3const Form = () => {
4  const [formData, setFormData] = React.useState({
5    firstName: '',
6    lastName: '',
7    email: '',
8    comments: '',
9    isFriendly: true,
10  });
11
12  const handleChange = (event) => {
13    const { name, value, type, checked } = event.target;
14    setFormData((prevFormData) => {
15      return {
16        ...prevFormData,
17        [name]: type === 'checkbox' ? checked : value,
18      };
19    });
20  };
21
22  return (
23    <form>
24      <input
25        type="text"
26        placeholder="First Name"
27        onChange={handleChange}
28        name="firstName"
29        value={formData.firstName}
30      />
31      <input
32        type="text"
33        placeholder="Last Name"
34        onChange={handleChange}
35        name="lastName"
36        value={formData.lastName}
37      />
38      <input
39        type="email"
40        placeholder="Email"
41        onChange={handleChange}
42        name="email"
43        value={formData.email}
44      />
45      <textarea
46        value={formData.comments}
47        placeholder="Comments"
48        onChange={handleChange}
49        name="comments"
50      />
51      <input
52        type="checkbox"
53        id="isFriendly"
54        checked={formData.isFriendly}
55        onChange={handleChange}
56        name="isFriendly"
57      />
58      <label htmlFor="isFriendly">Are you friendly?</label>
59      <br />
60    </form>
61  );
62};
63
64export default Form;

表單範例:Radio

製作 React 表單的 Radio 時,我們可以對 checked 屬性判斷 formData.employment 是否等於選取到的 value,以呈現哪一個選項是被選取的。

1import React from 'react';
2
3const Form = () => {
4  const [formData, setFormData] = React.useState({
5    firstName: '',
6    lastName: '',
7    email: '',
8    comments: '',
9    isFriendly: true,
10    employment: '',
11  });
12  console.log(formData.employment);
13
14  const handleChange = (event) => {
15    const { name, value, type, checked } = event.target;
16    setFormData((prevFormData) => {
17      return {
18        ...prevFormData,
19        [name]: type === 'checkbox' ? checked : value,
20      };
21    });
22  };
23
24  return (
25    <form>
26      <fieldset>
27        <legend>Current employment status</legend>
28
29        <input
30          type="radio"
31          id="unemployed"
32          name="employment"
33          value="unemployed"
34          checked={formData.employment === 'unemployed'}
35          onChange={handleChange}
36        />
37        <label htmlFor="unemployed">Unemployed</label>
38        <br />
39
40        <input
41          type="radio"
42          id="part-time"
43          name="employment"
44          value="part-time"
45          checked={formData.employment === 'part-time'}
46          onChange={handleChange}
47        />
48        <label htmlFor="part-time">Part-time</label>
49        <br />
50
51        <input
52          type="radio"
53          id="full-time"
54          name="employment"
55          value="full-time"
56          checked={formData.employment === 'full-time'}
57          onChange={handleChange}
58        />
59        <label htmlFor="full-time">Full-time</label>
60        <br />
61      </fieldset>
62    </form>
63  );
64};
65
66export default Form;

表單範例:Select & Option

在 HTML 中 Select 與 Option 會透過 selected 屬性標註被選取到的選項,例如:

1<select>
2  <option selected value="seal">Seal</option>
3</select>

但是在 React 中,因為我們要控制狀態,所以必須想辦法把 State 綁定上去。

我們新增狀態 favColor 把它綁定到 <select>value 屬性上,並且透過 onChange 事件更新狀態。

1const [formData, setFormData] = React.useState({
2  firstName: '',
3  lastName: '',
4  email: '',
5  comments: '',
6  isFriendly: true,
7  employment: '',
8  favColor: '',
9});
10console.log(formData.favColor);

跟其他範例一樣新增 HTML name 屬性,用 ES6 Computed Property 作為 setState 更新的 key,另外也新增了空值的預設選項。

1<select
2  id="favColor"
3  value={formData.favColor}
4  onChange={handleChange}
5  name="favColor"
6>
7  <option value="">-- Choose --</option>
8  <option value="red">Red</option>
9  <option value="orange">Orange</option>
10  <option value="yellow">Yellow</option>
11  <option value="green">Green</option>
12  <option value="blue">Blue</option>
13  <option value="indigo">Indigo</option>
14  <option value="violet">Violet</option>
15</select>

最後來送出表單吧

在 HTML 中,如果在 <form> 裡面放 <button> 預設就會是 type="submit",網頁會透過這個按鈕去送出表單。

1<form method="POST" action="somePhp.php">
2  <button>Submit</button>
3</form>

以下則是我們在 React 送出表單的做法,我們透過 Submit Button (Default type in form) 去觸發 onSumbit 事件來監聽表單送出,然後執行我們自訂的函式。

1<form onSubmit="handleSubmit">
2  <button>Submit</button>
3</form>

然而,如果表單沒有寫 action 的話,默認 HTML 會重整頁面,並且在網址後面加一段 Query String 例如:

/index.html?firstName=Sean&lastName=Huang&email=&comments=&isFriendly=on&favColor=red

這是預設的行為,但是我們在 React 當然不想這麼做,所以這裡先加個 event.preventDefault() 阻止預設行為,然後再呼叫 API 送表單資料給後端。

1function handleSubmit() {
2  event.preventDefault();
3  submitToApi(formData);
4}

Recap

  • Event listeners
  • State
  • Conditional rendering
  • Forms