【React入門】シンプルなTODOアプリの作り方 – 前編【プロジェクト作成〜タスク表示】
はじめに
こんにちは、優也@月収100万を目指して月100時間勉強する男です。
これまで4回に渡って、Reactの基礎知識を学んできました。環境構築、useState、イベントハンドラ、map関数…これらの知識がすべて揃ったので、いよいよ実際にTODOアプリを作っていきます!
前編となる今回は、プロジェクトのセットアップから、タスクの一覧表示までを実装します。まずは動くものを作って、次回以降で機能を追加していく形で進めます。
3回のシリーズで、完全に動作するTODOアプリを完成させましょう!
今回作るもの
前編(今回)で実装する機能
- ✅ プロジェクトのセットアップ
- ✅ 初期データの準備
- ✅ タスク一覧の表示
- ✅ 未完了/完了タスクの分離表示
中編(次回)で実装する機能
- タスクの追加機能
後編(次々回)で実装する機能
- タスクの削除機能
- タスクの完了機能
プロジェクトの作成
まずは新しいプロジェクトを作成します。
Viteでプロジェクト作成
環境構築の方法を忘れた方は、記事2: Vite環境構築を参照してください。
npm create vite@latest設定:
- プロジェクト名:
my-todo-app - フレームワーク:
React - バリアント:
JavaScript
依存パッケージのインストール
cd my-todo-app
npm install開発サーバーの起動
bash
npm run devブラウザで http://localhost:5173/ を開いて、Viteのデフォルト画面が表示されればOKです。
不要なファイルを削除
Viteが自動生成したファイルの一部は不要なので削除します。
- src/App.css
- src/index.css
- src/assets/react.svg
- public/vite.svg
src/App.jsx を空にする
src/App.jsxを以下のコードに書き換えます:
function App() {
return (
<div>
<h1>TODO APP</h1>
</div>
);
}
export default App;src/main.jsx を修正
src/main.jsxから不要なインポートを削除:
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)ブラウザに「TODO APP」と表示されればOKです!

初期データの準備
まずは表示するための初期データを用意します。
App.jsx にstateを追加
import { useState } from 'react';
function App() {
const [tasks, setTasks] = useState([
{ id: 1, taskName: '朝食を作る', completed: false },
{ id: 2, taskName: '買い物に行く', completed: false },
{ id: 3, taskName: 'ブログを書く', completed: true }
]);
return (
<div>
<h1>TODO APP</h1>
</div>
);
}
export default App;データ構造の説明
各タスクは以下の3つのプロパティを持ちます:
- id: タスクの一意な識別子(数値)
- taskName: タスクの名前(文字列)
- completed: 完了状態(true/false)
この構造で、タスクの管理をシンプルに行えます。
タスク一覧を表示する
map関数を使ってタスクを一覧表示します。
全タスクを表示
import { useState } from 'react';
function App() {
const [tasks, setTasks] = useState([
{ id: 1, taskName: '朝食を作る', completed: false },
{ id: 2, taskName: '買い物に行く', completed: false },
{ id: 3, taskName: 'ブログを書く', completed: true }
]);
return (
<div>
<h1>TODO APP</h1>
<h2>タスク一覧</h2>
<table border="1">
<thead>
<tr>
<th>タスク名</th>
<th>状態</th>
</tr>
</thead>
<tbody>
{tasks.map((task) => (
<tr key={task.id}>
<td>{task.taskName}</td>
<td>{task.completed ? '完了' : '未完了'}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
export default App;ポイント
map関数で配列を繰り返し処理key={task.id}で一意のキーを指定task.completed ? '完了' : '未完了'で状態を表示(三項演算子)
ブラウザを確認すると、3つのタスクが表形式で表示されているはずです!

未完了と完了を分けて表示
実際のTODOアプリでは、未完了タスクと完了タスクを分けて表示したいですね。
filter と map を組み合わせる
import { useState } from 'react';
function App() {
const [tasks, setTasks] = useState([
{ id: 1, taskName: '朝食を作る', completed: false },
{ id: 2, taskName: '買い物に行く', completed: false },
{ id: 3, taskName: 'ブログを書く', completed: true }
]);
return (
<div>
<h1>TODO APP</h1>
<h2>未完了タスク</h2>
<table border="1">
<thead>
<tr>
<th>タスク名</th>
</tr>
</thead>
<tbody>
{tasks
.filter((task) => !task.completed)
.map((task) => (
<tr key={task.id}>
<td>{task.taskName}</td>
</tr>
))}
</tbody>
</table>
<h2>完了タスク</h2>
<table border="1">
<thead>
<tr>
<th>タスク名</th>
</tr>
</thead>
<tbody>
{tasks
.filter((task) => task.completed)
.map((task) => (
<tr key={task.id}>
<td>{task.taskName}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
export default App;
コードの説明
未完了タスク:
tasks.filter((task) => !task.completed)completedがfalseのタスクだけを抽出
完了タスク:
tasks.filter((task) => task.completed)completedがtrueのタスクだけを抽出
これで、未完了タスク2件、完了タスク1件が別々に表示されます!
空のリストに対応する
タスクがない場合のメッセージも追加しましょう。
import { useState } from 'react';
function App() {
const [tasks, setTasks] = useState([
{ id: 1, taskName: '朝食を作る', completed: false },
{ id: 2, taskName: '買い物に行く', completed: false },
{ id: 3, taskName: 'ブログを書く', completed: true }
]);
const incompleteTasks = tasks.filter((task) => !task.completed);
const completedTasks = tasks.filter((task) => task.completed);
return (
<div>
<h1>TODO APP</h1>
<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;ポイント
filterの結果を変数に保存(incompleteTasks,completedTasks)length === 0でタスクの有無を判定- 三項演算子でメッセージまたはテーブルを表示

現在のコード全体
ここまでで完成したコードは以下の通りです:
import { useState } from 'react';
function App() {
const [tasks, setTasks] = useState([
{ id: 1, taskName: '朝食を作る', completed: false },
{ id: 2, taskName: '買い物に行く', completed: false },
{ id: 3, taskName: 'ブログを書く', completed: true }
]);
const incompleteTasks = tasks.filter((task) => !task.completed);
const completedTasks = tasks.filter((task) => task.completed);
return (
<div>
<h1>TODO APP</h1>
<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;コード行数: 約60行
まとめ
TODOアプリの前編として、タスク一覧の表示機能を実装しました。
今回実装した機能:
- ✅ プロジェクトのセットアップ
- ✅ 初期データの準備
- ✅ タスク一覧の表示
- ✅ 未完了/完了タスクの分離表示
- ✅ 空のリスト対応
使った技術:
- useState: タスクデータの管理
- map: 配列を繰り返し表示
- filter: 条件に合うタスクを抽出
- 三項演算子: 条件分岐
次回の中編では、タスクの追加機能を実装します。入力フォームを作って、新しいタスクを追加できるようにしましょう!
この記事のシリーズ:
- 【React入門】未経験からReactを学んで最初のTODOアプリを作った話
- 【2025年版】Viteで始めるReact開発環境の構築方法
- 【React基礎】useStateとは?初心者向けに分かりやすく解説
- 【React】イベントハンドラの書き方完全ガイド
- 【React】map関数でリストをレンダリングする方法
- 【React入門】TODOアプリの作り方 – 前編 ← 今ここ
- 【React入門】TODOアプリの作り方 – 中編(次回)
あとがき
月100時間の学習時間の中で、今月はReactに集中して取り組んでいます。Instagramでは、フリーランスとして月収100万円を目指す道のりを、リアルタイムで発信していきますので、ぜひフォローしてください!
Instagram: @hooded_yuya_100man
