---
# System prepended metadata

title: 第五週

---

# 第五週

本頁連結：https://hackmd.io/5foB4HVoT9GKTwvaJBkdKQ

## 完課獎勵說明

完整獎勵說明：https://hackmd.io/ry4GF6KWROKbx9cLf51bkA

- 2/16 前 50%：超過 100 人挑戰成功，全班直播加碼授權一年
    - 前完成課程報到 -> 第四週主線
- 2/23 前 60%：框架菁英班直播 → 主題：Next.js 起步走
    - 完成課程報到 -> 第六週主線 或 第五週主線 + 心得牆
- 3/9 前 80%：框架菁英班錄影(目前以 Vue 為主，React 可參考主題包含：從零建構 API、Cloud flare、前端工程師如何寫出好履歷)
    - 完成課程報到 -> 第七週主線 或 第六週主線 + 心得牆
- 100%：數位完賽獎狀

## 提醒：作業繳交說明

- 所有繳交期限 3/23
- **最終延長繳交條件**：3/23 前完整繳交（沒有被直接退件），可延長批改至 4/13
    - 直接退件的原因：
        - 助教下載後，`npm run dev` 無法運行
        - 作品不完整（作品內容有缺、有大量假字）


### 本週重點

- Hook
    - 自定義 Hook
    - 使用外部 Hooks：https://github.com/streamich/react-use
- React Hook Form
- React 開發優化：
    - 局部 Loading
    - 全畫面 Loading
    - Loading State 管理
    - https://www.npmjs.com/package/react-loading
    - https://www.npmjs.com/package/react-spinners
- useContext → 可自行看影音課程，後續會跳 redux toolkit


## 關於 Hooks

### 1. Hook 的概念

> Hooks 實現細粒度的邏輯拆分，讓功能更小、更純粹，元件僅將需要的功能引入，或者將重複的邏輯進行封裝。
> 

可以參考[課程教學](https://courses.hexschool.com/courses/react-video-course1/lectures/53655925)，簡單概念如下：

1. 先撰寫一份可運作的程式碼
2. 可嘗試將相關的邏輯抽出，定義成 use… 作為獨立 hook
3. 嘗試匯入運行，確認無誤後，可建立成獨立檔案

> 剛開始，可以放到 hooks 資料夾內，並使用 use….js 命名即可


### 使用現成的 Hooks

參考：https://github.com/streamich/react-use

範例：

1. useMouse
2. *useDropArea*
3. useDebounce

## React Hook Form

### 基本表單知識

常見表單標籤

- form
- label
- input
- button

### React Hook Form

#### 基本表單觀念

1. form 、 action 、submit
2. button
3. label 與 input


#### React 常見表單問題

0. 基礎表單輸入
1. checbox 單選與複選
2. select 預設值
3. 混合（炸）

#### 為什麼要使用 React Hook Form

1. 因為原有的 React 開發表單相對困難很多：參考 - https://courses.hexschool.com/courses/react-video-course1/lectures/53655949
2. 包含錯誤回饋的部分

### 使用 React Hook Form

**基礎使用**

1. 安裝 React Hook Form：npm install react-hook-form
2. 初始化 React Hook Form

```jsx
import { useForm } from "react-hook-form"

function Component() {
  const {
    register, // 用來註冊表單元素
    handleSubmit // 用來處理表單提交
  } = useForm();
  
  const onSubmit = (data) => { // 表單送出實際的資料內容
    console.log(data);
  };
  return ()
}

```

1. 將表單元素套用至 input 上

```jsx=
<form onSubmit={handleSubmit(onSubmit)}>
    <input {...register("firstName")} />
    <input {...register("lastName")} />
    <button type="submit">送出</button>
</form>
```

- 預設值定義方式

```jsx
const {
  register,
  handleSubmit
} = useForm({
  // 使用參數 defaultValues
  defaultValues: {
    firstName: "John",
    lastName: "Doe"
  }
})
```

- 各類表單元素套用方式

```jsx
{/* 各種表單元素，基本套用方式 */}
<input {...register("email")} />
<select {...register("category")}>
  <option value="">Select...</option>
  <option value="option1">Option 1</option>
  <option value="option2">Option 2</option>
</select>
{/* 單選 Checkbox */}
<input type="checkbox" {...register("isChecked")} /> Checkbox
{/* 多選 Checkbox */}
<label>
  <input type="checkbox" {...register("like")} value="鍋燒意麵" /> Option 1
</label>
<label>
  <input type="checkbox" {...register("like")} value="炒麵" /> Option 2
</label>
<label>
  <input type="checkbox" {...register("like")} value="漢堡" /> Option 3
</label>

{/* 單選 Radio */}
<input type="radio" {...register('gender')} value="男" /> 男
<input type="radio" {...register('gender')} value="女" /> 女
```

**監聽當前行為**

影音課程中有兩種方法：https://courses.hexschool.com/courses/react-video-course1/lectures/53655956）

1. 取出 Watch 行為

```

import { useForm, useWatch } from "react-hook-form"
```

2. 取出 Control

```jsx
const {
  register,
  handleSubmit,
  control, // 判斷控制的表單
} = useForm({
  ...
});
```

3. 將值取出，並呈現在畫面上

```jsx
const watchForm = useWatch({
  control,
});
useEffect(() => {
  console.log(watchForm);
}, [watchForm]);
```

**驗證方法（基礎）**

1. 驗證 required （必填，如果沒有填寫則無法送出

```jsx
<input type="text" {
  ...register('username', {
    required: true,
  })
} />
```

2. 錯誤回饋，取出 erros

```jsx
const {
  register, // 用來註冊表單元素
  handleSubmit,  // 用來處理表單提交
  control, // 判斷控制的表單
  formState: { errors }, // 錯誤訊息
} = useForm({
  ...
});

console.log(errors); // 當有錯誤時，這裡會出現資訊
```

3. 將錯誤呈現在畫面上

```jsx
<span>錯誤訊息：{
  errors.username ? '這個欄位為必填' : ''
}</span>
```

4. 可選：是否要立即做判斷

```jsx
const {
  ...
} = useForm({
  ...
  mode: 'onChange', // 表單驗證的時機
});
  
```

**驗證方法：進階**

常見驗證技巧

參考資源：https://react-hook-form.com/docs/useform/register

```jsx
<input
  type="password"
  {...register("password", {
    required: "密碼是必填項目",
    minLength: {
      value: 6,
      message: "密碼長度至少需為 6 個字元",
    },
    maxLength: {
      value: 12,
      message: "密碼長度不得超過 12 個字元",
    },
  })}
/>
<span>
  {errors.password ? errors.password.message : ""}
</span>
<br />

<input
  type="email"
  {...register("email", {
    required: "Email 是必填項目",
    pattern: {
      value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
      message: "請輸入有效的 Email 格式",
    },
  })}
/>
<span>
  {errors.email ? errors.email.message : ""}
</span>
<br />

<input type="text" {
  ...register('phone', {
    required: '請填寫手機號碼',
    pattern: {
      value: /^[0-9]{10}$/,
      message: '手機號碼格式錯誤',
    },
  })
} />
<span>錯誤訊息：{
  errors.phone ? errors.phone.message : ''
}</span>
```

- 正規表達式可搭配 AI 使用 （前後補上 `/`）
    
![image](https://hackmd.io/_uploads/H12UZneu1x.png)

### 錯誤樣式

```
className={
  `${errors['email'] ? 'error-state' : ''}`
}
```

## Loading 效果開發

- 全畫面 Loading
    - 可搭配：https://www.npmjs.com/package/react-spinners
    - https://www.npmjs.com/package/react-loading
- 單一元素 Loading
    - 可搭配：https://getbootstrap.com/docs/5.3/components/spinners/
- 列表單一元素 Loading


## 本週額外挑戰

- 預習章節閱讀：[連結](https://rpg.hexschool.com/#/training/12062543649513962870/board/content/12062543649513962883_12062543649513962890)
    - 環境建置（完整看完）
    - Router
- 作業連結：https://rpg.hexschool.com/#/training/12062543649513962870/board/content/12062543649513962871_12062543649513962892?tid=12062543649528478358
    - 助教作業解說（週日上午 10:00）
    ![image](https://hackmd.io/_uploads/BkXeKpsSke.png)
- 團隊任務：
    - https://rpg.hexschool.com/#/training/12062543649513962870/board/content/12062543649513962879_12062543649513962909