React を使ってシンプルな TODO 管理アプリを作る

# はじめに

今回は、React を使ってシンプルな TODO 管理アプリを作成します。機能としては、以下の3つが含まれています。

- タスク追加機能
- タスク完了チェック
- タスク削除機能

さらに、簡単な CSS を用いてアプリの見た目も整えます。プログラムの各部分を詳しく説明していきます。

## 完成イメージ
![スクリーンショット 2024-09-30 18.00.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75802/5ad0177e-96ab-0b98-83a3-be0a754afb09.png)


完成したアプリでは、タスクを追加して、完了済みのタスクにチェックを入れたり、不要なタスクを削除したりできます。

## 1. React プロジェクトの作成

まずは、React プロジェクトを作成します。以下のコマンドを実行して、React アプリをセットアップします。

```bash
npx create-react-app my-todo-app
cd my-todo-app
npm start
```

これで React アプリの基盤ができました。

## 2. プログラムの実装

次に、`src/App.js` を以下の内容に書き換えます。

### `App.js`

```javascript
import React, { useState } from "react";
import './App.css';  // CSSファイルをインポート

function App() {
  const [tasks, setTasks] = useState([]);  // タスクを管理するためのstate
  const [newTask, setNewTask] = useState("");  // 新規タスクの入力内容を管理するstate

  // タスク追加の関数
  const addTask = () => {
    if (newTask.trim() === "") return;  // 空のタスクは追加しない
    const newTaskObject = { id: Date.now(), text: newTask, isCompleted: false };  // タスクオブジェクトを作成
    setTasks([...tasks, newTaskObject]);  // 既存のタスクリストに追加
    setNewTask("");  // 入力フィールドをクリア
  };

  // タスク完了のチェックを切り替える関数
  const toggleTaskCompletion = (id) => {
    setTasks(
      tasks.map((task) =>
        task.id === id ? { ...task, isCompleted: !task.isCompleted } : task
      )
    );
  };

  // タスクを削除する関数
  const deleteTask = (id) => {
    setTasks(tasks.filter((task) => task.id !== id));  // 指定したid以外のタスクを残す
  };

  return (
    <div className="app-container">
      <h1 className="app-title">TODO 管理アプリ</h1>

      <div className="task-input-container">
        <input
          type="text"
          value={newTask}
          onChange={(e) => setNewTask(e.target.value)}
          placeholder="タスクを入力"
          className="task-input"
        />
        <button onClick={addTask} className="add-button">追加</button>
      </div>

      <ul className="task-list">
        {tasks.map((task) => (
          <li key={task.id} className="task-item">
            <input
              type="checkbox"
              checked={task.isCompleted}
              onChange={() => toggleTaskCompletion(task.id)}
              className="task-checkbox"
            />
            <span
              className={`task-text ${task.isCompleted ? "completed" : ""}`}
            >
              {task.text}
            </span>
            <button
              className="delete-button"
              onClick={() => deleteTask(task.id)}
            >
              削除
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App;
```

### プログラムの説明

#### 1. タスク管理用の State
`useState` フックを使って、2つの state を管理しています。

```javascript
const [tasks, setTasks] = useState([]);
const [newTask, setNewTask] = useState("");
```

- `tasks` は、タスクのリストを保持する配列です。
- `newTask` は、ユーザーが新規タスクを入力するための入力内容を保持します。

#### 2. タスク追加機能
`addTask` 関数では、ユーザーが入力したタスクをリストに追加しています。

```javascript
const addTask = () => {
  if (newTask.trim() === "") return;
  const newTaskObject = { id: Date.now(), text: newTask, isCompleted: false };
  setTasks([...tasks, newTaskObject]);
  setNewTask(""); // 入力フィールドをクリア
};
```

- 新しいタスクオブジェクトには、`id`（ユニークな識別子）、`text`（タスクの内容）、`isCompleted`（完了状態）を含んでいます。
- タスクを追加した後、入力フィールドをクリアします。

#### 3. タスク完了チェック機能
各タスクに対して完了状態を切り替えるために、`toggleTaskCompletion` 関数を使用しています。

```javascript
const toggleTaskCompletion = (id) => {
  setTasks(
    tasks.map((task) =>
      task.id === id ? { ...task, isCompleted: !task.isCompleted } : task
    )
  );
};
```

- タスクの `id` を使って、対象のタスクの `isCompleted` 状態を反転させています。
- `map` 関数を使って、タスクリスト全体を走査し、対象のタスクのみを更新します。

#### 4. タスク削除機能
`deleteTask` 関数では、指定した `id` を持つタスクを削除します。

```javascript
const deleteTask = (id) => {
  setTasks(tasks.filter((task) => task.id !== id));
};
```

- `filter` 関数を使って、削除したいタスク以外のタスクを残す処理を行っています。

## 3. スタイルの追加

続いて、見た目を整えるための CSS を追加します。`src/App.css` ファイルを作成し、以下の内容を追加してください。

### `App.css`

```css
.app-container {
  max-width: 600px;
  margin: 0 auto;
  padding: 20px;
  background-color: #f5f5f5;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

.app-title {
  text-align: center;
  font-size: 2em;
  color: #333;
  margin-bottom: 20px;
}

.task-input-container {
  display: flex;
  justify-content: space-between;
  margin-bottom: 20px;
}

.task-input {
  width: 75%;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
  font-size: 1em;
}

.add-button {
  padding: 10px 20px;
  background-color: #4CAF50;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.add-button:hover {
  background-color: #45a049;
}

.task-list {
  list-style-type: none;
  padding: 0;
}

.task-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background-color: #fff;
  padding: 10px;
  border-bottom: 1px solid #ddd;
  border-radius: 4px;
  margin-bottom: 10px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
}

.task-checkbox {
  margin-right: 10px;
}

.task-text {
  flex-grow: 1;
  margin-right: 20px;
}

.task-text.completed {
  text-decoration: line-through;
  color: #aaa;
}

.delete-button {
  padding: 5px 10px;
  background-color: #f44336;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.delete-button:hover {
  background-color: #e53935;
}
```

## 4. 動作確認

すべてのファイルを保存し、`npm start` を実行してブラウザでアプリを確認してみてください。スタイリッシュな TODO 管理アプリが動作しているはずです！