【React入門】シンプルなTODOアプリの作り方 – 後編【削除・完了機能の実装】
はじめに
こんにちは、優也@月収100万を目指して月100時間勉強する男です。
前回・前々回の記事で、プロジェクトのセットアップ、タスク一覧の表示、タスクの追加機能を実装してきました。今回の後編では、削除機能と完了機能を実装して、TODOアプリを完成させます!
これでタスクを追加・削除・完了できる、実用的なTODOアプリが完成します。
これまでのおさらい
前編・中編で実装した機能:
- ✅ プロジェクトのセットアップ
- ✅ タスク一覧の表示
- ✅ 未完了/完了タスクの分離表示
- ✅ タスク追加機能
今回実装する機能
後編(今回)で実装する機能:
- ✅ タスク削除機能
- ✅ タスク完了機能
- ✅ 完了タスクの表示
前回のコードを準備
まず、前回完成したコードから始めます。
import { useState } from 'react';
function App() {
const [inputTask, setInputTask] = useState('');
const [tasks, setTasks] = useState([
{ id: 1, taskName: '朝食を作る', completed: false },
{ id: 2, taskName: '買い物に行く', completed: false },
{ id: 3, taskName: 'ブログを書く', completed: true }
]);
const addTask = () => {
if (inputTask.trim() === '') return;
const newId = tasks.length === 0
? 1
: Math.max(...tasks.map(task => task.id)) + 1;
const newTask = {
id: newId,
taskName: inputTask,
completed: false
};
setTasks([...tasks, newTask]);
setInputTask('');
};
const incompleteTasks = tasks.filter((task) => !task.completed);
const completedTasks = tasks.filter((task) => task.completed);
return (
<div>
<h1>TODO APP</h1>
<h2>タスクの追加</h2>
<div>
<input
type="text"
value={inputTask}
onChange={(e) => setInputTask(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') {
addTask();
}
}}
placeholder="タスクを入力"
/>
<button type="button" onClick={addTask}>追加</button>
</div>
<h2>未完了タスク</h2>
{incompleteTasks.length === 0 ? (
<p>未完了タスクはありません</p>
) : (
<table border="1">
<thead>
<tr>
<th>タスク名</th>
</tr>
</thead>
<tbody>
{incompleteTasks.map((task) => (
<tr key={task.id}>
<td>{task.taskName}</td>
</tr>
))}
</tbody>
</table>
)}
<h2>完了タスク</h2>
{completedTasks.length === 0 ? (
<p>完了タスクはありません</p>
) : (
<table border="1">
<thead>
<tr>
<th>タスク名</th>
</tr>
</thead>
<tbody>
{completedTasks.map((task) => (
<tr key={task.id}>
<td>{task.taskName}</td>
</tr>
))}
</tbody>
</table>
)}
</div>
);
}
export default App;ここから機能を追加していきます。

削除機能を実装
まず、タスクを削除する機能を作ります。
削除関数を追加
const deleteTask = (id) => {
setTasks(tasks.filter((task) => task.id !== id));
};コードの説明
tasks.filter((task) => task.id !== id)filter: 条件に合う要素だけを残すtask.id !== id: 削除対象のID以外を残す- 結果: 指定したIDのタスクが除外された新しい配列
削除ボタンを追加
未完了タスクのテーブルに削除ボタンを追加します:
<tbody>
{incompleteTasks.map((task) => (
<tr key={task.id}>
<td>{task.taskName}</td>
<td>
<button type="button" onClick={() => deleteTask(task.id)}>
削除
</button>
</td>
</tr>
))}
</tbody>完了タスクのテーブルにも削除ボタンを追加します:
<tbody>
{completedTasks.map((task) => (
<tr key={task.id}>
<td>{task.taskName}</td>
<td>
<button type="button" onClick={() => deleteTask(task.id)}>
削除
</button>
</td>
</tr>
))}
</tbody>ポイント
onClick={() => deleteTask(task.id)}- アロー関数でラップする
task.idを引数として渡す- これでクリックされたタスクのIDが
deleteTaskに渡される
完了機能を実装
次に、タスクを完了状態にする機能を作ります。
完了関数を追加
const completeTask = (id) => {
setTasks(
tasks.map((task) =>
task.id === id ? { ...task, completed: true } : task
)
);
};コードの説明
tasks.map((task) =>
task.id === id ? { ...task, completed: true } : task
)map: すべてのタスクを処理task.id === id: 対象のタスクかチェック{ ...task, completed: true }: 対象ならcompletedをtrueに変更: task: 対象外ならそのまま
スプレッド構文(...task)で既存のプロパティをコピーし、completedだけを上書きしています。
完了ボタンを追加
未完了タスクのテーブルに完了ボタンを追加します:
<tbody>
{incompleteTasks.map((task) => (
<tr key={task.id}>
<td>{task.taskName}</td>
<td>
<button type="button" onClick={() => completeTask(task.id)}>
完了
</button>
</td>
<td>
<button type="button" onClick={() => deleteTask(task.id)}>
削除
</button>
</td>
</tr>
))}
</tbody>テーブルのヘッダーを調整
ボタンが増えたので、テーブルのヘッダーも調整します。
未完了タスク
<thead>
<tr>
<th>タスク名</th>
<th>完了</th>
<th>削除</th>
</tr>
</thead>完了タスク
<thead>
<tr>
<th>タスク名</th>
<th>削除</th>
</tr>
</thead>完了タスクには「完了ボタン」は不要なので、削除ボタンのみです。
完成したコード全体
ここまでで完成したコードは以下の通りです:
import { useState } from 'react';
function App() {
const [inputTask, setInputTask] = useState('');
const [tasks, setTasks] = useState([
{ id: 1, taskName: '朝食を作る', completed: false },
{ id: 2, taskName: '買い物に行く', completed: false },
{ id: 3, taskName: 'ブログを書く', completed: true }
]);
const addTask = () => {
if (inputTask.trim() === '') return;
const newId = tasks.length === 0
? 1
: Math.max(...tasks.map(task => task.id)) + 1;
const newTask = {
id: newId,
taskName: inputTask,
completed: false
};
setTasks([...tasks, newTask]);
setInputTask('');
};
const deleteTask = (id) => {
setTasks(tasks.filter((task) => task.id !== id));
};
const completeTask = (id) => {
setTasks(
tasks.map((task) =>
task.id === id ? { ...task, completed: true } : task
)
);
};
const incompleteTasks = tasks.filter((task) => !task.completed);
const completedTasks = tasks.filter((task) => task.completed);
return (
<div>
<h1>TODO APP</h1>
<h2>タスクの追加</h2>
<div>
<input
type="text"
value={inputTask}
onChange={(e) => setInputTask(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') {
addTask();
}
}}
placeholder="タスクを入力"
/>
<button type="button" onClick={addTask}>追加</button>
</div>
<h2>未完了タスク</h2>
{incompleteTasks.length === 0 ? (
<p>未完了タスクはありません</p>
) : (
<table border="1">
<thead>
<tr>
<th>タスク名</th>
<th>完了</th>
<th>削除</th>
</tr>
</thead>
<tbody>
{incompleteTasks.map((task) => (
<tr key={task.id}>
<td>{task.taskName}</td>
<td>
<button type="button" onClick={() => completeTask(task.id)}>
完了
</button>
</td>
<td>
<button type="button" onClick={() => deleteTask(task.id)}>
削除
</button>
</td>
</tr>
))}
</tbody>
</table>
)}
<h2>完了タスク</h2>
{completedTasks.length === 0 ? (
<p>完了タスクはありません</p>
) : (
<table border="1">
<thead>
<tr>
<th>タスク名</th>
<th>削除</th>
</tr>
</thead>
<tbody>
{completedTasks.map((task) => (
<tr key={task.id}>
<td>{task.taskName}</td>
<td>
<button type="button" onClick={() => deleteTask(task.id)}>
削除
</button>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
);
}
export default App;コード行数: 約110行

動作確認
ブラウザで以下を確認してください:
- ✅ タスクを追加できる
- ✅ 未完了タスクの「完了」ボタンで完了タスクに移動
- ✅ 未完了タスクの「削除」ボタンでタスクが削除される
- ✅ 完了タスクの「削除」ボタンでタスクが削除される
すべて動作していれば、TODOアプリ完成です! 🎉
まとめ
3回のシリーズでTODOアプリを完成させました!
実装した全機能:
- ✅ タスク一覧の表示
- ✅ 未完了/完了タスクの分離表示
- ✅ タスクの追加
- ✅ タスクの削除
- ✅ タスクの完了
使った技術:
- useState: タスクデータと入力の管理
- map: 配列をリスト表示
- filter: 条件に合うタスクを抽出、削除
- スプレッド構文: 配列への追加、オブジェクトの更新
- onChange/onClick: ユーザー操作の処理
コード行数: 約110行
たった110行のコードで、実用的なTODOアプリが完成しました。これがReactの力です!
次回からは、配列操作の詳細やよくあるエラーについて解説していきます。
この記事のシリーズ:
- 【React入門】未経験からReactを学んで最初のTODOアプリを作った話
- 【2025年版】Viteで始めるReact開発環境の構築方法
- 【React基礎】useStateとは?初心者向けに分かりやすく解説
- 【React】イベントハンドラの書き方完全ガイド
- 【React】map関数でリストをレンダリングする方法
- 【React入門】TODOアプリの作り方 – 前編
- 【React入門】TODOアプリの作り方 – 中編
- 【React入門】TODOアプリの作り方 – 後編 ← 今ここ
- 【実例で学ぶ】Reactで配列のstateを更新する5つのパターン(次回)
あとがき
月100時間の学習時間の中で、今月はReactに集中して取り組んでいます。Instagramでは、フリーランスとして月収100万円を目指す道のりを、リアルタイムで発信していきますので、ぜひフォローしてください!
Instagram: @hooded_yuya_100man
