これまでの知識(コンポーネント、props、state、イベント、リスト表示)をまとめて使います。まずは最小のTODOを作り、次回以降の拡張にも耐える設計にします。
完成イメージと仕様
最小仕様は次の通りです。
- 入力欄にTODOを入れる
- 追加ボタンでリストに追加
- 各行に削除ボタンを付ける
追加要件(任意):
- 空文字は追加しない
- Enterで追加
- 件数表示
まずは1ファイルで作る(App.jsx)
App.jsx を以下に置き換えて動かしてみます。
import { useState } from "react"
export default function App() {
const [todos, setTodos] = useState([])
const [text, setText] = useState("")
const addTodo = () => {
const trimmed = text.trim()
if (!trimmed) return
const newTodo = {
id: Date.now(),
text: trimmed,
}
setTodos([...todos, newTodo])
setText("")
}
const removeTodo = (id) => {
setTodos(todos.filter((t) => t.id !== id))
}
return (
<div style={{ padding: 16 }}>
<h1>TODO</h1>
<div style={{ display: "flex", gap: 8 }}>
<input
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="やることを入力"
/>
<button onClick={addTodo}>追加</button>
</div>
<p>件数: {todos.length}</p>
<ul>
{todos.map((t) => (
<li key={t.id} style={{ display: "flex", gap: 8 }}>
<span>{t.text}</span>
<button onClick={() => removeTodo(t.id)}>削除</button>
</li>
))}
</ul>
</div>
)
}
ポイント:
- 追加:
setTodos([...todos, newTodo]) - 削除:
filterで除外 - 入力:
valueとonChangeでstateと同期
よくある改善:Enterで追加
入力欄に onKeyDown を入れてEnterで追加できます。
onKeyDown={(e) => {
if (e.key === "Enter") addTodo()
}}
このようにイベントを足すだけで操作性が上がります。
次の改善:コンポーネント分割
TODOが動いたら、次の分割をするとReactらしい設計になります。
TodoInput:入力と追加ボタンTodoList:一覧表示TodoItem:1行
次回は「次に学ぶ」回で、API通信やRouterへ進む道筋を示します。
練習問題
- TODOが0件のとき「まだTODOがありません」と表示してみましょう。
- 完了チェック(true/false)を付けて、完了は打ち消し線にしてみましょう。
- localStorageに保存して、リロードしても残るようにしてみましょう(ヒント:useEffect)。


コメント