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; ```