DAY3(2025/06/16)
[TOC]
# DAY3(2025/06/16)React
## 進度日誌
- 完成父子元件資料傳遞與互動應用,學會 callback 傳遞事件
## 筆記區📘 React
### ▶ 父子元件資料傳遞 & 互動應用
#### 🧠 學習重點
- 元件之間怎麼「傳遞資料」(props 向下傳遞)
- 子元件怎麼把「事件」回傳給父元件(callback 向上傳遞)
- 實作元件互動邏輯
---
#### 🏗️ 父子元件互動練習
#### 1️⃣ 目標效果
- App 畫面上有一個「按我」按鈕(在子元件)
- 每按一次,父元件的 `count` 就會 +1
- `count` 數字顯示在父元件 App 裡
---
#### 2️⃣ 父元件 App.tsx
```tsx
import React,{ useState } from 'react';
import Hello from './components/Hello';
import HelloWorldButton from './components/HelloWorldButton';
import ChildButton from './components/ChildButton';
function App() {
const[count,setCount]=useState<number>(0);
const handleChildClick = () =>{
setCount(count + 1);
}
return (
<>
<div>
<h2>第一個元件</h2>
<Hello name="宸維(Shiki)" age={21} />
</div>
<br />
<div>
<h2>第二個元件</h2>
<HelloWorldButton />
</div>
<div>
<h2>第三個元件(父子元件互動練習)</h2>
<p>目前點擊次數:{count}</p>
<ChildButton onButtonClick={handleChildClick}/>
</div>
</>
);
}
export default App;
```
---
#### 3️⃣ 子元件 ChildButton.tsx
```tsx
import React from 'react';
// 定義 props 型別:接收一個 callback 函式
type ChildButtonProps = {
onButtonClick: () => void;
};
const ChildButton: React.FC<ChildButtonProps> = ({ onButtonClick }) => {
return (
<button onClick={onButtonClick}>按我!</button>
);
};
export default ChildButton;
```
##### onButtonClick: () => void 是什麼意思?
- onButtonClick 是一個「函數」
- 它沒有參數(())
- 它不回傳任何東西(void)
---
### ▶ 列表渲染(map)、資料狀態管理與互動
#### 🧠 學習重點
- 如何用陣列在畫面上動態產生一堆元件
- 怎麼用 `useState` 管理一整包「資料清單」
- 如何新增、刪除、切換完成 Todo 等常見前端互動
---
#### 🏗️ 任務目標:做一個簡易 TodoList
#### 1️⃣ 目標效果
- 畫面有一個輸入框 + 新增按鈕
- 底下列出所有任務(可用陣列 state 管理)
- 每個任務旁邊有一個「刪除」按鈕
---
#### 2️⃣ App.tsx(主程式)範例
```tsx
import React, { useState } from 'react';
type Todo = {
id: number;
text: string;
};
function App() {
const [todos, setTodos] = useState<Todo[]>([]); // 任務清單
const [input, setInput] = useState<string>(''); // 輸入框狀態
// 新增任務
const handleAdd = () => {
if (input.trim() === '') return;
setTodos([...todos, { id: Date.now(), text: input }]);
setInput('');
};
// 刪除任務
const handleDelete = (id: number) => {
setTodos(todos.filter(todo => todo.id !== id));
};
return (
<div>
<h1>Todo List</h1>
<input
value={input}
onChange={e => setInput(e.target.value)}
placeholder="輸入任務..."
/>
<button onClick={handleAdd}>新增</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo.text} <button onClick={() => handleDelete(todo.id)}>刪除</button>
</li>
))}
</ul>
</div>
);
}
export default App;
```
##### 1. ``useState<Todo[]>([])`` 是什麼?
- `useState` 是宣告一個「狀態」用的 Hook。
- ``<Todo[]>`` 指的是「這個狀態的型別」是Todo 物件組成的陣列。
- ``[]`` 是初始值,表示一開始這個清單是空的。
##### 2. ``setTodos([...todos, { id: Date.now(), text: input }]);`` 是什麼?
- ``...todos``:把「目前已經有的所有任務」複製一份出來
- ``{ id: Date.now(), text: input }``:新增一個新的任務物件
- ``[...todos, 新任務]``:把新任務接在所有舊任務的最後
###### 補充說明
- `todos`:現在的全部任務(例如 ``[{id:1,text:'A'},{id:2,text:'B'}]``)
- ``...todos``:把全部任務展開(這叫「展開運算子」spread operator)
##### 3. `setTodos(todos.filter(todo => todo.id !== id)); `是什麼?
- `todos.filter(...)``:篩選出你想保留的任務
- `todo => todo.id !== id`:只有 id 不等於要刪掉的那個,才留下來
- `setTodos(...)``:更新任務清單,把舊的換成過濾後的新清單
---
#### 3️⃣ 整合App.tsx
```tsx=
import React, { useState } from 'react';
import Hello from './components/Hello';
import HelloWorldButton from './components/HelloWorldButton';
import ChildButton from './components/ChildButton';
type Todo = {
id: number;
text: string;
};
function App() {
// 做一個簡易 TodoList
const [todos, setTodos] = useState<Todo[]>([]); // 任務清單
const [input, setInput] = useState<string>(''); // 輸入框狀態
const handleAdd = () => { //新增任務
if (input.trim() === '') return;
setTodos([...todos, { id: Date.now(), text: input }]);
setInput('');
};
const handleDelete = (id: number) => { //刪除任務
setTodos(todos.filter(todo => todo.id !== id));
};
//第三元件
const [count, setCount] = useState<number>(0); //預設Count = 0,setCount處理後續變更後count的值
const handleChildClick = () => {
setCount(count + 1);
}
return (
//<> ... </> 是 Fragment,不會多包一層 div,語法簡潔,React 專案很常見!
<>
<div>
<h2>第一個元件</h2>
<Hello name="宸維(Shiki)" age={21} />
</div>
<br />
<div>
<h2>第二個元件</h2>
<HelloWorldButton />
</div>
<br />
<div>
<h2>第三個元件(父子元件互動練習)</h2>
<p>目前點擊次數:{count}</p>
<ChildButton onButtonClick={handleChildClick} />
</div>
<br />
<div>
<h2>Todo List</h2>
<input
value={input}
onChange={e => setInput(e.target.value)}
placeholder="輸入任務..."
/>
<button onClick={handleAdd}>新增</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo.text} <button onClick={() => handleDelete(todo.id)}>刪除</button>
</li>
))}
</ul>
</div>
</>
);
}
export default App;
```