Reactで作るTODOアプリ開発シリーズ第5回
この記事では
Reactでのリスト表示と条件付きレンダリングの実装方法
を学習していきます。
前回はイベントハンドリングとフォーム処理について学習しました。今回は、データを動的に表示し、条件に応じてUIを制御する方法について学んでいきましょう。
リスト表示と条件付きレンダリングは、Reactアプリケーションで最もよく使われる機能の一つです。これらを適切に実装することで、ユーザーにとって使いやすく、見た目も美しいアプリケーションを作ることができます。
- map関数を使ったリスト表示の基本
- keyプロパティの重要性と使い方
- 条件付きレンダリング(&&演算子、三項演算子)
- TODOリストの表示とフィルタリング
- 完了/未完了の切り替え表示

リスト表示と条件付きレンダリングを理解することで、データに応じて動的に変化するUIを作ることができるようになります!








TODOアプリ開発シリーズのまとめ記事はこちら
前回までのコード
第4回で作成したコードを確認しましょう。以下のファイル構成になっています。
ファイル構造
todo-app/
├── src/
│ ├── App.js
│ ├── App.css
│ ├── TodoItem.js
│ ├── TodoList.js
│ ├── TodoHeader.js
│ └── TodoForm.js
App.js
import React, { useState } from 'react';
import './App.css';
import TodoHeader from './TodoHeader';
import TodoList from './TodoList';
import TodoForm from './TodoForm';
function App() {
const [todos, setTodos] = useState([
{ id: 1, text: "Reactを学ぶ", completed: false, priority: "high" },
{ id: 2, text: "TODOアプリを作る", completed: true, priority: "medium" },
{ id: 3, text: "データベース連携", completed: false, priority: "low" },
{ id: 4, text: "デプロイする", completed: false, priority: "medium" }
]);
const completedCount = todos.filter(todo => todo.completed).length;
// TODOの完了状態を切り替える関数
const toggleTodo = (id) => {
setTodos(prevTodos =>
prevTodos.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
)
);
};
// 新しいTODOを追加する関数
const addTodo = (newTodo) => {
setTodos(prevTodos => [...prevTodos, newTodo]);
};
return (
<div className="App">
<TodoHeader
title="React TODOアプリ"
totalCount={todos.length}
completedCount={completedCount}
/>
<TodoForm onAddTodo={addTodo} />
<TodoList todos={todos} onToggle={toggleTodo} />
</div>
);
}
export default App;
TodoForm.js
import React, { useState } from 'react';
function TodoForm({ onAddTodo }) {
const [formData, setFormData] = useState({
text: '',
priority: 'medium'
});
const [error, setError] = useState('');
const handleChange = (event) => {
const { name, value } = event.target;
setFormData(prevData => ({
...prevData,
[name]: value
}));
if (error) {
setError('');
}
};
const handleSubmit = (event) => {
event.preventDefault();
if (!formData.text.trim()) {
setError('TODOの内容を入力してください');
return;
}
if (formData.text.length > 100) {
setError('TODOの内容は100文字以下で入力してください');
return;
}
const newTodo = {
id: Date.now(),
text: formData.text.trim(),
completed: false,
priority: formData.priority
};
onAddTodo(newTodo);
setFormData({
text: '',
priority: 'medium'
});
setError('');
};
return (
<form onSubmit={handleSubmit} className="todo-form">
<div className="form-group">
<input
type="text"
name="text"
value={formData.text}
onChange={handleChange}
placeholder="新しいTODOを入力してください"
className="todo-input"
/>
<select
name="priority"
value={formData.priority}
onChange={handleChange}
className="priority-select"
>
<option value="low">低優先度</option>
<option value="medium">中優先度</option>
<option value="high">高優先度</option>
</select>
<button type="submit" className="add-button">
追加
</button>
</div>
{error && <p className="error-message">{error}</p>}
</form>
);
}
export default TodoForm;
App.css
.todo-form {
margin-bottom: 30px;
padding: 20px;
background-color: #f8f9fa;
border-radius: 8px;
}
.form-group {
display: flex;
gap: 10px;
align-items: center;
}
.todo-input {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
.todo-input:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
}
.priority-select {
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
background-color: white;
}
.add-button {
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.2s;
}
.add-button:hover {
background-color: #0056b3;
}
.add-button:disabled {
background-color: #6c757d;
cursor: not-allowed;
}
.error-message {
color: #dc3545;
margin-top: 10px;
font-size: 14px;
}
リスト表示の基本
実際に手を動かす前に、まずは必要な知識について解説をしていきます。
Reactで配列のデータを表示するには、map
関数を使用します。map
関数は、配列の各要素に対して処理を行い、新しい配列を返します。
基本的なリスト表示
function BasicList() {
const items = ["りんご", "みかん", "バナナ", "ぶどう"];
return (
<div>
<h3>果物リスト</h3>
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
オブジェクトのリスト表示
function ObjectList() {
const users = [
{ id: 1, name: "田中太郎", age: 25 },
{ id: 2, name: "佐藤花子", age: 30 },
{ id: 3, name: "鈴木一郎", age: 28 }
];
return (
<div>
<h3>ユーザーリスト</h3>
<ul>
{users.map(user => (
<li key={user.id}>
{user.name} ({user.age}歳)
</li>
))}
</ul>
</div>
);
}
keyプロパティの重要性
Reactでリストを表示する際、各要素に一意のkey
プロパティを指定する必要があります。これは、Reactが要素を効率的に更新するために使用されます。
keyプロパティの基本
function KeyExample() {
const [items, setItems] = useState([
{ id: 1, text: "アイテム1" },
{ id: 2, text: "アイテム2" },
{ id: 3, text: "アイテム3" }
]);
const addItem = () => {
const newItem = {
id: Date.now(),
text: `アイテム${items.length + 1}`
};
setItems(prevItems => [...prevItems, newItem]);
};
return (
<div>
<button onClick={addItem}>アイテムを追加</button>
<ul>
{items.map(item => (
<li key={item.id}>{item.text}</li>
))}
</ul>
</div>
);
}
keyプロパティの注意点
// ❌ 避けるべき書き方(indexをkeyとして使用)
function BadKeyExample() {
const items = ["りんご", "みかん", "バナナ"];
return (
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li> // 配列の順序が変わる可能性がある場合は危険
))}
</ul>
);
}
// ✅ 推奨される書き方(一意のIDを使用)
function GoodKeyExample() {
const items = [
{ id: 1, name: "りんご" },
{ id: 2, name: "みかん" },
{ id: 3, name: "バナナ" }
];
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
条件付きレンダリング
条件付きレンダリングは、特定の条件に基づいて異なるコンテンツを表示する機能です。
基本的な条件付きレンダリング
1. &&演算子を使用
function ConditionalRendering() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [userName, setUserName] = useState("");
return (
<div>
{isLoggedIn && (
<div>
<h2>ようこそ、{userName}さん!</h2>
<button onClick={() => setIsLoggedIn(false)}>
ログアウト
</button>
</div>
)}
{!isLoggedIn && (
<div>
<h2>ログインしてください</h2>
<input
type="text"
placeholder="ユーザー名"
value={userName}
onChange={(e) => setUserName(e.target.value)}
/>
<button onClick={() => setIsLoggedIn(true)}>
ログイン
</button>
</div>
)}
</div>
);
}
2. 三項演算子を使用
function TernaryExample() {
const [isLoading, setIsLoading] = useState(true);
const [data, setData] = useState(null);
return (
<div>
{isLoading ? (
<div>読み込み中...</div>
) : (
<div>
<h3>データが読み込まれました</h3>
<p>{data}</p>
</div>
)}
</div>
);
}
3. 複数の条件分岐
function MultipleConditions() {
const [status, setStatus] = useState("loading");
const renderContent = () => {
switch (status) {
case "loading":
return <div>読み込み中...</div>;
case "success":
return <div>成功しました!</div>;
case "error":
return <div>エラーが発生しました</div>;
default:
return <div>不明な状態です</div>;
}
};
return (
<div>
<div>現在の状態: {status}</div>
{renderContent()}
<button onClick={() => setStatus("success")}>
成功にする
</button>
<button onClick={() => setStatus("error")}>
エラーにする
</button>
</div>
);
}
リスト表示と条件付きレンダリングの組み合わせ
実際のアプリケーションでは、リスト表示と条件付きレンダリングを組み合わせて使用することが多いです。
フィルタリング機能の実装
function FilteredList() {
const [filter, setFilter] = useState("all");
const [items, setItems] = useState([
{ id: 1, text: "買い物に行く", completed: false },
{ id: 2, text: "本を読む", completed: true },
{ id: 3, text: "運動する", completed: false },
{ id: 4, text: "料理を作る", completed: true }
]);
const filteredItems = items.filter(item => {
if (filter === "all") return true;
if (filter === "completed") return item.completed;
if (filter === "pending") return !item.completed;
return true;
});
return (
<div>
<div>
<button onClick={() => setFilter("all")}>
すべて
</button>
<button onClick={() => setFilter("completed")}>
完了済み
</button>
<button onClick={() => setFilter("pending")}>
未完了
</button>
</div>
<ul>
{filteredItems.map(item => (
<li key={item.id}>
<span style={{
textDecoration: item.completed ? 'line-through' : 'none'
}}>
{item.text}
</span>
{item.completed && <span> ✓</span>}
</li>
))}
</ul>
{filteredItems.length === 0 && (
<p>該当するアイテムがありません</p>
)}
</div>
);
}
【実践】フィルタリング機能と条件付きレンダリングの実装
ここからは実際に手を動かして、TODOアプリにフィルタリング機能と条件付きレンダリングを追加していきましょう。
1. TodoFilterコンポーネントの作成
src/TodoFilter.js
ファイルを新規作成し、以下のコードを記述してください。
import React from 'react';
function TodoFilter({ filter, onFilterChange }) {
return (
<div className="todo-filter">
<button
className={`filter-button ${filter === 'all' ? 'active' : ''}`}
onClick={() => onFilterChange('all')}
>
すべて
</button>
<button
className={`filter-button ${filter === 'pending' ? 'active' : ''}`}
onClick={() => onFilterChange('pending')}
>
未完了
</button>
<button
className={`filter-button ${filter === 'completed' ? 'active' : ''}`}
onClick={() => onFilterChange('completed')}
>
完了済み
</button>
</div>
);
}
export default TodoFilter;
2. App.jsの修正
既存のsrc/App.js
を以下のように修正してください。
import React, { useState } from 'react';
import './App.css';
import TodoHeader from './TodoHeader';
import TodoList from './TodoList';
import TodoForm from './TodoForm';
import TodoFilter from './TodoFilter';
function App() {
const [todos, setTodos] = useState([
{ id: 1, text: "Reactを学ぶ", completed: false, priority: "high" },
{ id: 2, text: "TODOアプリを作る", completed: true, priority: "medium" },
{ id: 3, text: "データベース連携", completed: false, priority: "low" },
{ id: 4, text: "デプロイする", completed: false, priority: "medium" }
]);
const [filter, setFilter] = useState('all');
// フィルタリングされたTODOリスト
const filteredTodos = todos.filter(todo => {
if (filter === 'all') return true;
if (filter === 'completed') return todo.completed;
if (filter === 'pending') return !todo.completed;
return true;
});
const completedCount = todos.filter(todo => todo.completed).length;
// TODOの完了状態を切り替える関数
const toggleTodo = (id) => {
setTodos(prevTodos =>
prevTodos.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
)
);
};
// 新しいTODOを追加する関数
const addTodo = (newTodo) => {
setTodos(prevTodos => [...prevTodos, newTodo]);
};
return (
<div className="App">
<TodoHeader
title="React TODOアプリ"
totalCount={todos.length}
completedCount={completedCount}
/>
<TodoForm onAddTodo={addTodo} />
<TodoFilter filter={filter} onFilterChange={setFilter} />
<TodoList todos={filteredTodos} onToggle={toggleTodo} />
</div>
);
}
export default App;
3. TodoList.jsの修正
既存のsrc/TodoList.js
を以下のように修正してください。
import React from 'react';
import TodoItem from './TodoItem';
function TodoList({ todos, onToggle }) {
// TODOが存在しない場合の表示
if (todos.length === 0) {
return (
<div className="empty-state">
<p>TODOがありません。新しいTODOを追加してください。</p>
</div>
);
}
return (
<div className="todo-list">
{todos.map((todo) => (
<TodoItem
key={todo.id}
id={todo.id}
text={todo.text}
completed={todo.completed}
priority={todo.priority}
onToggle={onToggle}
/>
))}
</div>
);
}
export default TodoList;
4. TodoItem.jsの修正
既存のsrc/TodoItem.js
を以下のように修正してください。
import React from 'react';
function TodoItem({ id, text, completed, priority = "medium", onToggle }) {
return (
<div className={`todo-item priority-${priority}`}>
<input
type="checkbox"
checked={completed}
onChange={() => onToggle(id)}
/>
<span style={{
textDecoration: completed ? 'line-through' : 'none'
}}>
{text}
</span>
{completed && (
<span className="completed-badge">完了</span>
)}
{!completed && (
<span className="pending-badge">未完了</span>
)}
</div>
);
}
export default TodoItem;
5. スタイルの追加
既存のsrc/App.css
に以下のスタイルを追加してください。
.todo-filter {
display: flex;
justify-content: center;
gap: 10px;
margin-bottom: 20px;
}
.filter-button {
padding: 8px 16px;
border: 1px solid #ddd;
background-color: white;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
}
.filter-button:hover {
background-color: #f8f9fa;
}
.filter-button.active {
background-color: #007bff;
color: white;
border-color: #007bff;
}
.empty-state {
text-align: center;
padding: 40px;
color: #666;
font-style: italic;
}
.completed-badge {
background-color: #28a745;
color: white;
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
margin-left: 10px;
}
.pending-badge {
background-color: #ffc107;
color: #212529;
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
margin-left: 10px;
}
動作確認
実装が完了したら、開発サーバーが起動している状態でブラウザを確認してください。以下の機能が動作することを確認できます。








- フィルターボタンでTODOリストを絞り込み表示
- 完了/未完了のバッジ表示
- TODOが存在しない場合のメッセージ表示
- アクティブなフィルターボタンのハイライト表示
FAQ
まとめ
今回は、Reactでのリスト表示と条件付きレンダリングについて学習しました。
map
関数を使ったリスト表示により、データに応じて動的にUIを生成することができるようになりました。また、key
プロパティの重要性を理解し、効率的なリスト更新の方法を学びました。
条件付きレンダリングにより、ユーザーの操作やデータの状態に応じて適切なUIを表示することができるようになりました。



次回は、TODOの編集と削除機能について学習していきます。CRUD操作の実装を通じて、より実践的なアプリケーション開発を体験していきましょう!
この記事が少しでもお役に立ったなら何よりです。次回もお楽しみに!
コメント