REACT CORE · 98
Events、Controlled Forms、Lists、Keys:互動如何回到 State
這一課把 Browser event、form input、array rendering 接回 React state model。重點不是 JSX event 名稱,而是 event → validation → state transition → re-render。
Learning outcomes
- 能追蹤 React event handler 的執行順序。
- 能建立 controlled form。
- 能用 stable key 維持 list item identity。
- 能用 immutable state transition 新增 / 修改 / 刪除項目。
1. Event handler:傳 function,不是執行結果
<button
onClick={handleSave}
>
Save
</button>
不是:
<button
onClick={handleSave()}
>
後者會在 render 時先執行。
2. Controlled input:UI value 來自 state
const [
title,
setTitle
] = useState("");
<input
value={title}
onChange={event =>
setTitle(
event.target.value
)
}
/>
state title→ input valueUser typing→ onChangesetTitle
3. Submit pipeline
function handleSubmit(
event
) {
event.preventDefault();
const cleaned =
title.trim();
if (!cleaned) {
return;
}
setCourses(current => [
...current,
{
id:
crypto.randomUUID(),
title: cleaned,
score: null
}
]);
setTitle("");
}
這裡可以明確分成 Browser default behavior、input normalization、validation、state transition。
4. List rendering 與 key
{courses.map(course => (
<CourseCard
key={course.id}
course={course}
/>
))}
key 幫 React 在 sibling list 中追蹤 identity。它不是給你的 component 當普通 prop 使用。
5. 為什麼動態清單不宜隨便用 index key?
如果 item 會新增、刪除、排序,index 可能代表不同實體。這會讓 React 對 item identity 的理解和你的 domain identity 不一致,造成 state 對錯項目等問題。
6. Project checkpoint:Course Dashboard v3
function removeCourse(id) {
setCourses(current =>
current.filter(
course =>
course.id !== id
)
);
}
function addCourse(title) {
const cleaned =
title.trim();
if (!cleaned) return;
setCourses(current => [
...current,
{
id:
crypto.randomUUID(),
title: cleaned,
score: null
}
]);
}
新增 / 刪除都變成明確 state transition。
Debug evidence:表單「送出後重新整理」
如果 submit 後整頁 reload,先檢查 handler 是否真的綁到 onSubmit、是否執行 preventDefault();不要先懷疑 state API。
Knowledge check
- controlled input 的 single source of truth 是誰?
- 為什麼
onClick={fn}和onClick={fn()}不同? - 什麼情況 index key 特別容易出問題?
- 完成 add / remove / edit 三種 immutable state transition。