---
# System prepended metadata

title: "\U0001F4D8 React 入門講義-2"

---

## 2025/9/05
### 📘 React 第 2 堂 — Hook 入門 & UI 進階

官方對應章節：《Describing the UI》（後半）＋《Adding Interactivity》（開頭）
#### 1. Hook 基礎

**什麼是 Hook？**

- React 元件就是一個 function，每次畫面更新時會「重新執行」。
- 如果只用普通變數，function 每次重播都會「失憶」，之前的值會消失。
- **Hook（例如 useState）就是 React 提供的工具，讓元件能記住狀態。**

**Hook 的兩個限制**

- 只能在元件最外層呼叫
    - React 是靠「呼叫順序」來記錄狀態。
    - 如果把 Hook 放在 if / for 裡，順序會亂掉，React 就會搞混。
- 只能在 React 元件或其他 Hook 裡呼叫
    - Hook 只能存在於 React 管理的 function 中，這樣 React 才能保存和更新狀態。

**範例**

```jsx
// ❌ 錯誤：放在 if 裡
function BadCounter({ show }) {
  if (show) {
    const [count, setCount] = useState(0);
  }
  return <p>這樣會壞掉</p>;
}

// ✅ 正確：放在最外層
function GoodCounter({ show }) {
  const [count, setCount] = useState(0);
  if (!show) return null;
  return <button onClick={() => setCount(c => c + 1)}>+1 ({count})</button>;
}

```

**舉例說明**

`useState` 會幫你建立一個「狀態值」以及「更新這個狀態的方法」。

常見的寫法是：

```jsx
const [count, setCount] = useState(0);
```
1. `useState(0)`

- `useState` 是 React 的 **Hook**，用來在函式元件裡建立「狀態 (state)」。
- 這裡的 `0` 是「初始值」。
- 所以一開始 `sharedLikes = 0`。


2. 解構賦值 `[count, setCount]`

    `useState` 會回傳一個 **陣列**，裡面有兩個東西：

    1. **目前的狀態值** → 這裡叫 `count`
    2. **更新狀態的函式** → 這裡叫 `setCount`

    所以你用陣列解構，把它們取名出來。

    等價於：

    ```jsx
    const stateArray = useState(0);
    const count = stateArray[0];      // 當前狀態值
    const setCount = stateArray[1];  // 更新狀態的函式

    ```

3. 怎麼用？

- **讀取狀態** → 用 `Count`
- **更新狀態** → 呼叫 `setCount`
4. 和一般 js 的count 差別？

- **js** →  要手動更新 DOM  `Count`才會更新
- **react** 
    - React 會更新 count 的值。
    - React 會觸發「元件重新渲染」，畫面才會顯示最新的數字


---

#### 2. useState 進階：快照陷阱 & 函式型更新

**快照陷阱**

- React 的狀態更新不是立即的，而是「排隊等下次一併處理」。
- 多次 `setCount(count + 1)` 會用到「同一個舊值」，結果錯誤。

```jsx
// ❌ 快照陷阱：只會 +1
function CounterSnapshot() {
  const [count, setCount] = useState(0);

  const handleAddThree = () => {
    setCount(count + 1);
    setCount(count + 1);
    setCount(count + 1);
  };

  return <button onClick={handleAddThree}>+3（陷阱）: {count}</button>;
}

```

**函式型更新（正確解法）**

- 使用 `setCount(c => c + 1)`，保證每次都用最新的值。

```jsx
// ✅ 修正版：函式型更新
function CounterFixed() {
  const [count, setCount] = useState(0);

  const handleAddThree = () => {
    setCount(c => c + 1);
    setCount(c => c + 1);
    setCount(c => c + 1);
  };

  return <button onClick={handleAddThree}>+3（正確）: {count}</button>;
}

```

**重點**

- React 更新是非同步批次處理。
- 需要依賴前一個值時，務必使用「函式型更新」。

---

#### 3. 狀態隔離 vs 狀態共享

**狀態隔離**

- 每個元件自己有自己的 state。
- 彼此之間互不干擾。

```jsx
function MyCounter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(c => c + 1)}>+1 ({count})</button>;
}

function App() {
  return (
    <><MyCounter />
      <MyCounter />
      <MyCounter />
    </>
  );
}

```

**狀態共享**

- 把 state 提升到父元件集中管理。
- 子元件透過 props 使用相同的 state。

```jsx
function MyButton({ count, onClick }) {
  return <button onClick={onClick}>+1 ({count})</button>;
}

function App() {
  const [count, setCount] = useState(0);
  return (
    <><MyButton count={count} onClick={() => setCount(c => c + 1)} />
      <MyButton count={count} onClick={() => setCount(c => c + 1)} />
    </>
  );
}

```

**重點**

- 狀態隔離：元件各自有獨立資料。
- 狀態共享：父元件集中管理，子元件同步更新。

:test_tube: 
小測驗

- 兩種情況的重置如何設置?
---

#### 4. 條件式 Rendering

**概念**

- React 的判斷是「要不要產生元素」。
- 與傳統 `display:none` 不同，React 元素未被渲染就不會存在於 DOM。

**三種常用寫法**

```jsx
// if 語句
if (isLoggedIn) {
  return <p>歡迎回來！</p>;
}
return <p>請先登入</p>;

// 三元運算子 ? :
<p>{isLoggedIn ? "歡迎回來！" : "請先登入"}</p>

// && 短路
<div>
  <p>首頁</p>
  {isLoading && <p>載入中...</p>}
</div>

```

**(1) if 語句**

語法：

```jsx
if (條件) {
  return <元素A />;
} else {
  return <元素B />;
}

```

- `if` 是 **語句 (statement)**，不能直接寫在 JSX 中。
- 通常放在 `return` 之前，決定整個 Component 要渲染什麼。

---

**(2) 三元運算子 ? :**

語法：

```jsx
{條件 ? <元素A /> : <元素B />}

```

- 有三個部分：**條件 ? true時的值 : false時的值**。
- 是 **運算式 (expression)**，所以可以直接寫在 JSX 裡。
- 常用來處理「二選一」的情況。

---

 **(3) && 短路運算**

語法：

```jsx
{條件 && <元素A />}

```

- 如果 `條件 = true` → 顯示 `<元素A />`。
- 如果 `條件 = false` → React 忽略，不渲染任何東西。
- 適合「有就顯示、沒有就不顯示」。

---

**差異重點**

- **if** → 適合整段邏輯判斷（例如：載入中就直接 return，後面不渲染）。
- **`? :` 三元運算子** → 適合小範圍「二選一」。
- **`&&` 短路運算** → 適合「有就顯示，沒有就不顯示」。

:test_tube: 
小測驗

1. 如果 isLoggedIn = false，上面這一行會顯示什麼？
2. 如果 isLoading = true，整個畫面會怎樣？

---

#### 5. 列表渲染進階

**為什麼需要 key？**

- React 會比對舊陣列與新陣列，決定哪些需要更新。
- **key 是判斷元素身份的依據**，必須唯一且穩定。
- key 不正確 → React 搞錯元素 → 畫面錯位或輸入內容消失。

**錯誤示範：用 index 當 key**

```jsx
{todos.map((todo, i) => <li key={i}>{todo}</li>)}

```

**正確示範：用唯一 id**

```jsx
{todos.map(todo => <li key={todo.id}>{todo.text}</li>)}

```

**搭配排序與過濾**

```jsx
<ul>
  {todos
    .filter(todo => !todo.done)
    .sort((a, b) => a.text.localeCompare(b.text))
    .map(todo => <li key={todo.id}>{todo.text}</li>)}
</ul>

```

**React 相對於 JS 的優勢**

- 表單驗證：只新增錯誤提示，其他輸入框保持不變。
- 即時搜尋：只刪除不符合的項目，其他輸入框內容保留。
- 互動式表格：排序或刪除時，只更新有變動的 row，checkbox 與輸入值保持原狀。

**重點**

- key 是元素的「身分證字號」，必須唯一且穩定。
- React 只更新差異，不會整個清空再重建。

:test_tube: 
小測驗

1. 為什麼 key 不能用 index 當 key？
---

#### 6. 回應 Event（Responding to Events）

**概念**

- 在 React，可以在 JSX 中綁定 **事件處理函式 (event handler)**。
- Event handler 會在互動發生時觸發（例如：點擊、輸入、hover）。
- event handler 本質上就是 **function**，通常命名為 `handleXxx`。

---

**基本範例**

```jsx
export default function Button() {
  function handleClick() {
    alert("You clicked me!");
  }

  return (
    <button onClick={handleClick}>
      Click me
    </button>
  );
}

```

**重點**

- `onClick={handleClick}` → 傳遞函式，而不是呼叫它。
- React 會在點擊時才執行 `handleClick`。

---

**Inline handler 寫法**

```jsx
<button onClick={() => alert("You clicked me!")}>
  Click me
</button>

```

- 適合簡短的邏輯。
- 本質上是傳遞一個匿名函式。

---

**常見陷阱**

```jsx
// ❌ 錯誤：會在 render 時立即執行
<button onClick={handleClick()}>Click me</button>

// ✅ 正確：傳遞函式
<button onClick={handleClick}>Click me</button>

```

---

**使用 props 的 event handler**

```jsx
function AlertButton({ message, children }) {
  return (
    <button onClick={() => alert(message)}>
      {children}
    </button>
  );
}

export default function Toolbar() {
  return (
    <div>
      <AlertButton message="Playing!">Play Movie</AlertButton>
      <AlertButton message="Uploading!">Upload Image</AlertButton>
    </div>
  );
}

```

- event handler 可以使用 component 的 props。
- 同一個元件可以因為不同 props 而有不同行為。

---

**父子傳遞 event handler**

```jsx
function Button({ onClick, children }) {
  return <button onClick={onClick}>{children}</button>;
}

export default function Toolbar() {
  return (
    <div>
      <Button onClick={() => alert("Playing!")}>Play Movie</Button>
      <Button onClick={() => alert("Uploading!")}>Upload Image</Button>
    </div>
  );
}

```

- parent 可以把邏輯傳下去，child 只負責顯示樣式與結構。

---

**事件傳遞與停止**

```jsx
function Button({ onClick, children }) {
  return (
    <buttononClick={(e) => {
        e.stopPropagation(); // 阻止冒泡
        onClick();
      }}
    >
      {children}
    </button>
  );
}

export default function Toolbar() {
  return (
    <divclassName="Toolbar"
      onClick={() => alert("You clicked on the toolbar!")}
    >
      <Button onClick={() => alert("Playing!")}>Play Movie</Button>
      <Button onClick={() => alert("Uploading!")}>Upload Image</Button>
    </div>
  );
}

```

- 點擊按鈕 → 只觸發按鈕自己的事件，不會冒泡到外層 `<div>`。
- `e.stopPropagation()` 阻止事件傳遞。
- `e.preventDefault()` 則是阻止瀏覽器的預設行為（例如表單送出）。

---
**重點整理**

- **event handler 要傳遞函式，而不是呼叫**。
- 可以寫在外部，也可以 inline。
- 可以存取 props，讓相同元件行為不同。
- 可以從 parent 把事件處理邏輯傳給 child。
- 可以用 `stopPropagation` 或 `preventDefault` 控制事件傳遞與預設行為。

---

🧪 小測驗

1. 下列哪個寫法是錯誤的？
    
    ```jsx
    <button onClick={handleClick}>Click</button>
    <button onClick={() => handleClick()}>Click</button>
    <button onClick={handleClick()}>Click</button>
    
    ```
    
2. 點擊以下程式碼中的「Play Movie」按鈕會出現幾個 alert？
    
    ```jsx
    <div onClick={() => alert("Toolbar clicked!")}>
      <button onClick={() => alert("Playing!")}>Play Movie</button>
    </div>
    
    ```
    
3. 要如何阻止表單送出時自動刷新頁面？

---

#### 7. 小專案 Demo：SurveyForm 問卷填報系統

**功能**

- 新增 / 刪除 回覆

- 搜尋 / 篩選 回覆

- 排序 (依名字升冪 / 降冪)

- 受控表單驗證（姓名必填）

**程式碼**

（沿用 TodoListV2，不再重貼）

---

#### 8. 課堂總結

- Hook 是 React 記住狀態的方式
- useState 的陷阱與進階用法
- 狀態隔離 vs 狀態共享
- 條件式 Rendering 的三種寫法
- 列表渲染與 key 的重要性
- 受控元件處理表單輸入
- 綜合練習：TodoList v2