バックエンドエンジニアがReactコンポーネントについて理解するためにTODOアプリ作成してみた

# はじめに
仕事でReactを実装する機会に恵まれたのですが、ほとんど触ったことがないのでこのタイミングでReactについていろいろ学んでいきたいと思います。
今回は、Reactコンポーネントの設計と状態管理に焦点を当て、CodeSandboxでTODOアプリを作成してみました。
https://codesandbox.io/

完成イメージです。
![スクリーンショット 2023-09-25 20.48.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330132/f26dda3d-7a70-1690-07ca-b155954fbda7.png)

# Reactとは

Reactは、Metaが開発したオープンソースのJavaScriptライブラリで、ユーザーインターフェースを構築するためのツールです。Reactは、コンポーネントベースのアーキテクチャを採用しており、コンポーネントを組み合わせてUIを構築します。
まず、TODOアプリのコンポーネント設計から始めていきます。

# コンポーネント設計

Reactアプリケーションを構築するとき、1つの大きなコンポーネントではなく、小さなコンポーネントに分割することが重要になってくると思います。
これにより、コードの可読性が向上し、再利用性が高まります。
今回のTODOアプリの場合、次のようなコンポーネントを設計します。

- TodoList: TODOアイテムのリストを表示し、新しいTODOアイテムを追加できるフォームを提供します。
- TodoItem: 個々のTODOアイテムを表示し、完了状態をトグルできるコンポーネントです。
- TodoForm: 新しいTODOアイテムを入力し、追加するためのフォームです。

# TodoItemコンポーネント

まず、TodoItem コンポーネントから実装していきます。
これは、個々のTODOアイテムを表します。

```react
// TodoItem.js
import React from 'react';
import "./TodoItem.css";

function TodoItem({ todo, onToggle, onDelete }) {
  return (
    <div>
      <span
        style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
        onClick={() => onToggle(todo.id)}
      >
        {todo.text}
      </span>
      <button onClick={() => onDelete(todo.id)}>削除</button>
    </div>
  );
}

export default TodoItem;
```

```css
/* TodoItem.css */
/* TodoItemコンポーネントにスタイルを追加 */

li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px;
  border: 1px solid #ccc;
  margin: 5px 0;
  background-color: #fff;
  border-radius: 4px;
  cursor: pointer;
}

li:hover {
  background-color: #f5f5f5;
}

span {
  flex-grow: 1;
  cursor: pointer;
  text-decoration: ${(props) => (props.completed ? 'line-through' : 'none')};
  color: #333;
  font-size: 16px;
}

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

button:hover {
  background-color: #d32f2f;
}

```

TodoItem コンポーネントは、TODOアイテムのテキストを表示し、テキストがクリックされたときに onToggle 関数を呼び出して完了状態をトグルします。また、TODOアイテムを削除するためのボタンも提供しています。

# TodoFormコンポーネント

次に、新しいTODOアイテムを入力し、追加するための TodoForm コンポーネントを定義します。

```react
// TodoForm.js
import React, { useState } from 'react';
import "./TodoForm.css";

function TodoForm({ onSubmit }) {
  const [newTask, setNewTask] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    if (newTask.trim() !== '') {
      onSubmit(newTask);
      setNewTask('');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="新しいタスクを追加"
        value={newTask}
        onChange={(e) => setNewTask(e.target.value)}
      />
      <button type="submit">追加</button>
    </form>
  );
}

export default TodoForm;
```

```css
/* TodoForm.css */
/* TodoFormコンポーネントにスタイルを追加 */
form {
  margin: 20px 0;
  display: flex;
  justify-content: center;
}

input[type="text"] {
  padding: 10px;
  font-size: 16px;
  border: 1px solid #ccc;
  border-radius: 4px;
  width: 70%;
}

button[type="submit"] {
  padding: 10px 20px;
  font-size: 16px;
  background-color: #007bff;
  color: #fff;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  margin-left: 10px;
}

button[type="submit"]:hover {
  background-color: #0056b3;
}
```

TodoForm コンポーネントは、新しいタスクを入力し、フォームを送信するためのフォームを提供します。ユーザーがテキストを入力し、フォームを送信すると、指定された onSubmit 関数が呼び出され、新しいTODOアイテムが追加されます。

# TodoListコンポーネント

最後に、これらのコンポーネントを組み合わせて TodoList コンポーネントを作成します。

```react
// TodoList.js
import React, { useState } from 'react';
import TodoItem from './TodoItem';
import TodoForm from './TodoForm';
import "./TodoList.css";

function TodoList() {
  const [todos, setTodos] = useState([]);

  const handleAddTodo = (text) => {
    const newTodo = { id: Date.now(), text, completed: false };
    setTodos([...todos, newTodo]);
  };

  const handleToggleTodo = (todoId) => {
    const updatedTodos = todos.map((todo) =>
      todo.id === todoId ? { ...todo, completed: !todo.completed } : todo
    );
    setTodos(updatedTodos);
  };

  const handleDeleteTodo = (todoId) => {
    const updatedTodos = todos.filter((todo) => todo.id !== todoId);
    setTodos(updatedTodos);
  };

  return (
    <div>
      <h1>TODOリスト</h1>
      <TodoForm onSubmit={handleAddTodo} />
      <ul>
        {todos.map((todo) => (
          <TodoItem
            key={todo.id}
            todo={todo}
            onToggle={handleToggleTodo}
            onDelete={handleDeleteTodo}
          />
        ))}
      </ul>
    </div>
  );
}

export default TodoList;
```

```css
/* TodoList.css */
.App {
  text-align: center;
  font-family: Arial, sans-serif;
}

h1 {
  color: #333;
}

ul {
  list-style-type: none;
  padding: 0;
}
```


TodoList コンポーネントは、TODOアイテムのリストを表示し、新しいTODOアイテムを追加できるフォームを提供します。また、各TODOアイテムに対して TodoItem コンポーネントを使用して表示し、完了状態のトグルと削除の機能を提供します。

# ルートコンポーネント

最終的に、これらのコンポーネントをルートコンポーネントで統合します。

```react
// App.js
import React from 'react';
import TodoList from './TodoList';

function App() {
  return (
    <div className="App">
      <TodoList />
    </div>
  );
}

export default App;
```

TodoList コンポーネントがルートコンポーネントである App コンポーネントに組み込まれています。

# まとめ

今回は、Reactを使用してTODOアプリの作成を通してReactのコンポーネント設計と状態管理の基本を学びました。
バックエンドエンジニアとしてフロントエンドの技術に触れることは新鮮でわくわくするものがあります。
これを機会にReactについての理解を深めていきたいところです。