练
小项目实战:TODO List
Hands-On — A Minimal Todo App
把前面学的串起来:React 19 + TypeScript + Vite。功能很简单——加任务、删任务、切换完成状态。但麻雀虽小五脏俱全。
App.tsx —— 完整可跑
import { useState } from "react";
type Todo = { id: number; text: string; done: boolean };
export default function App() {
const [todos, setTodos] = useState<Todo[]>([]);
const [text, setText] = useState("");
// 加任务:返回新数组,别直接 push
const add = () => {
if (!text.trim()) return;
setTodos(prev => [...prev, { id: Date.now(), text, done: false }]);
setText("");
};
// 切换完成
const toggle = (id: number) =>
setTodos(prev => prev.map(t => t.id === id ? { ...t, done: !t.done } : t));
// 删除
const remove = (id: number) =>
setTodos(prev => prev.filter(t => t.id !== id));
return (
<main style={{ maxWidth: 480, margin: "40px auto", fontFamily: "sans-serif" }}>
<h1>TODO</h1>
<input
value={text}
onChange={e => setText(e.target.value)}
onKeyDown={e => e.key === "Enter" && add()}
placeholder="输入任务,回车添加"
style={{ width: "70%", padding: 8 }}
/>
<button onClick={add} style={{ padding: "8px 16px", marginLeft: 8 }}>添加</button>
<ul style={{ listStyle: "none", padding: 0, marginTop: 20 }}>
{todos.map(t => (
<li key={t.id} style={{
padding: "8px 0",
textDecoration: t.done ? "line-through" : "none",
opacity: t.done ? 0.5 : 1,
}}>
<input type="checkbox" checked={t.done} onChange={() => toggle(t.id)} />
{t.text}
<button onClick={() => remove(t.id)} style={{ float: "right" }}>删</button>
</li>
))}
</ul>
</main>
);
}
跑起来后你会发现:加任务、删任务、勾选完成,全程没碰 DOM,只在改 state。这就是 React 的开发方式——描述 UI 应该是什么样,React 负责画。