React / TODOアプリ / コンポーネント化

#コンポーネント化とは
機能ごとに分割して別ファイルにしておくことで、使い回せたり、状態の管理もしやすく拡張性があるので再利用も容易にできるというメリットがあります。

##前提

- `CodeSandbox`を使用

https://codesandbox.io/

##実装手順

| 実装順 | ページ内リンク |
|:----:|:-------------|
| 1 | [コンポーネント用のディレクトリ作成](#コンポーネント用のディレクトリ作成) |
| 2 | [テキストボックス](#テキストボックス) |
| 3 | [未完了TODOリスト](#未完了リスト) |
| 4 | [完了TODOリスト](#完了リスト) |

#コンポーネント用のディレクトリ作成

* `src`ディレクト配下に`components`ディレクトリを作成
    * これから作成する**コンポーネントファイル**は、このディレクトリに格納します。

#テキストボックス
**まずは、タスクの入力欄であるテキストボックスからコンポーネント化していきます。**

```react:src/components/InputTodo.jsx
import React from "react";

export const InputTodo = (props) => {
  const { todoText, onChange, onClick } = props;
  return (
    <div className="input-area">
      <input placeholder="TODOを入力" value={todoText} onChange={onChange} />
      <button onClick={onClick}>追加</button>
    </div>
  );
};
```

* テキストボックス用のコンポーネントファイル(`InpotTodo.jsx`)を作成し、`src/App.jsx`からテキストボックス部分のコードをコピ-し、貼り付けてから修正を行なっています。

```react
export const InputTodo = (props) => {
  const { todoText, onChange, onClick } = props;
```

* 親コンポーネント(`src/App.jsx`)に記述のある関数を`props`を用いて受け取っています。
    * 分割代入を使用することで、受け取った`props`の先頭に`props.`と書く必要がなくなるため、記述量が減り、コードの見通しが良くなります。

```react
<button onClick={onClick}>追加</button>

// 分割代入せずに使用する場合
<button onClick={props.onClick}>追加</button>
```

* `props`で受け取った関数を使用しています。

```react:src/App.jsx
import React, { useState } from "react";
import "./styles.css";
import { InputTodo } from "./components/InputTodo";

export const App = () => {
  const [todoText, setTodoText] = useState("");

  const [incompleteTodos, setIncompleteTodos] = useState([]);
  const [completeTodos, setCompleteTodos] = useState([]);

  const onChangeTodoText = (event) => setTodoText(event.target.value);

  const onClickAdd = () => {
    if (todoText === "") return;
    const newTodos = [...incompleteTodos, todoText];
    setIncompleteTodos(newTodos);
    setTodoText("");
  };
  .
  .
  .
  return (
    <>
      <InputTodo
        todoText={todoText}
        onChange={onChangeTodoText}
        onClick={onClickAdd}
      />
      .
      .
      .
```

**次に親コンポーネントである`App.jsx`ファイルを編集します。**

```react
import { InputTodo } from "./components/InputTodo";
```

* コンポーネント化した`InputTodo`ファイルのインポートを宣言します。

```react
<InputTodo
  todoText={todoText}
  onChange={onChangeTodoText}
  onClick={onClickAdd}
/>
```

* 先ほどコピーしたテキストボックス部分のコードを削除し、`<InputTodo />`に置き換えることでコンポーネントを呼び出すことができます。
* コンポーネント内には、`Input.jsx`に関数を渡すための記述をしています。
    * `porps`（任意の名前）に関数を代入しています -> `props名={関数名}`
        * `JavaScript`を渡すのでブランケット`{}`で関数を囲っています。

#未完了リスト
**次に未完了TODOリスト欄をコンポーネント化していきます。**

```react:src/components/IncompleteTodos.jsx
import React from "react";

export const IncompleteTodos = (props) => {
  const { todos, onClickComplete, onClickDelete } = props;
  return (
    <div className="incomplete-area">
      <p className="title">未完了のTODO</p>
      <ul>
        {todos.map((todo, index) => {
          return (
            <div key={todo} className="list-row">
              <li>{todo}</li>
              <button onClick={() => onClickComplete(index)}>完了</button>
              <button onClick={() => onClickDelete(index)}>削除</button>
            </div>
          );
        })}
      </ul>
    </div>
  );
};
```

```react:src/App.jsx
import React, { useState } from "react";
import "./styles.css";

import { InputTodo } from "./components/InputTodo";
import { IncompleteTodos } from "./components/IncompleteTodos"; // 追加

export const App = () => {
  .
  .
  .
  const onClickDelete = (index) => {
    const newTodos = [...incompleteTodos];
    newTodos.splice(index, 1);
    setIncompleteTodos(newTodos);
  };

  const onClickComplete = (index) => {
    const newIncompleteTodos = [...incompleteTodos];
    newIncompleteTodos.splice(index, 1);
    setIncompleteTodos(newIncompleteTodos);

    const newCompleteTodos = [...completeTodos, incompleteTodos[index]];
    setCompleteTodos(newCompleteTodos);
  };
  .
  .
  .
  return (
    <>
      .
      .
      .
      <IncompleteTodos
        todos={incompleteTodos}
        onClickComplete={onClickComplete}
        onClickDelete={onClickDelete}
      />
```

**先ほどと同様の実装のため、解説は割愛します。**

#完了リスト
**最後に完了TODOリスト欄をコンポーネント化していきます。**

```react:src/components/IncompleteTodos.jsx
import React from "react";

export const CompleteTodos = (props) => {
  const { todos, onClickBack } = props;
  return (
    <div className="complete-area">
      <p className="title">完了のTODO</p>
      <ul>
        {todos.map((todo, index) => {
          return (
            <div key={todo} className="list-row">
              <li>{todo}</li>
              <button onClick={() => onClickBack(index)}>戻る</button>
            </div>
          );
        })}
      </ul>
    </div>
  );
};
```

```react:src/App.jsx
import React, { useState } from "react";
import "./styles.css";

import { InputTodo } from "./components/InputTodo";
import { IncompleteTodos } from "./components/IncompleteTodos";
import { CompleteTodos } from "./components/CompleteTodos"; // 追加

export const App = () => {
  .
  .
  .
  const [completeTodos, setCompleteTodos] = useState([]);
  .
  .
  .
  const onClickBack = (index) => {
    const newCompleteTodos = [...completeTodos];
    newCompleteTodos.splice(index, 1);
    setCompleteTodos(newCompleteTodos);
  .
  .
  .
  return (
    <>
      .
      .
      .
      <CompleteTodos todos={completeTodos} onClickBack={onClickBack} />
```

**先ほどと同様の実装のため、解説は割愛します。**

#完成形のソースコード

```react:src/App.jsx
import React, { useState } from "react";
import "./styles.css";

import { InputTodo } from "./components/InputTodo";
import { IncompleteTodos } from "./components/IncompleteTodos";
import { CompleteTodos } from "./components/CompleteTodos";

export const App = () => {
  const [todoText, setTodoText] = useState("");

  const [incompleteTodos, setIncompleteTodos] = useState([]);
  const [completeTodos, setCompleteTodos] = useState([]);

  const onChangeTodoText = (event) => setTodoText(event.target.value);

  const onClickAdd = () => {
    if (todoText === "") return;
    const newTodos = [...incompleteTodos, todoText];
    setIncompleteTodos(newTodos);
    setTodoText("");
  };

  const onClickDelete = (index) => {
    const newTodos = [...incompleteTodos];
    newTodos.splice(index, 1);
    setIncompleteTodos(newTodos);
  };

  const onClickComplete = (index) => {
    const newIncompleteTodos = [...incompleteTodos];
    newIncompleteTodos.splice(index, 1);
    setIncompleteTodos(newIncompleteTodos);

    const newCompleteTodos = [...completeTodos, incompleteTodos[index]];
    setCompleteTodos(newCompleteTodos);
  };

  const onClickBack = (index) => {
    const newCompleteTodos = [...completeTodos];
    newCompleteTodos.splice(index, 1);
    setCompleteTodos(newCompleteTodos);

    const newIncompleteTodos = [...incompleteTodos, completeTodos[index]];
    setIncompleteTodos(newIncompleteTodos);
  };
  return (
    <>
      <InputTodo
        todoText={todoText}
        onChange={onChangeTodoText}
        onClick={onClickAdd}
      />
      <IncompleteTodos
        todos={incompleteTodos}
        onClickComplete={onClickComplete}
        onClickDelete={onClickDelete}
      />
      <CompleteTodos todos={completeTodos} onClickBack={onClickBack} />
    </>
  );
};
```
**このようにコンポーネント化したことで`return();`内のHTML部分がスッキリとしてコードの見通しが良くなったことが分かります。**

##最後に
実はじゃけぇさんが提供しているもう一本の教材も購入しています笑
中級者レベルと難易度も上がっているので、学習しながらステップアップしちゃいます！

https://www.udemy.com/course/react_stepup

##参考教材

https://www.udemy.com/course/modern_javascipt_react_beginner