create-react-appでTodoアプリを作成

# はじめに
create-react-appはReactで簡単にアプリを作成するためのツールです。
Reactのみで簡単なTodoアプリを作成したので備忘録としてまとめたいと思います。
なお、多少の環境構築が必要なので、よろしければ[この記事](https://qiita.com/anago1030/items/46831b7e0aad58eec07f)を参考にしてください。

また今回は**JSX**、**props**、**useState**の知識を扱います。この記事では説明を省きますので
ご了承ください。

# 挙動イメージ
![ezgif.com-gif-maker (10).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/772095/f535b57f-4c24-7c00-f2e7-e5036c87979c.gif)

* **1. Todoを入力したら未完了のTodoに反映される**
* **2. 未完了のTodoは完了のTodoに移動できる or 削除できる**
* **3. 完了のTodoは未完了のTodoに移動できる**

# 目次
[1.create-react-app](#1-create-react-app)
[2.HTMLとCSSで構造を作成](#2-HTMLとCSSで構造を作成)
[3.Reactを意識したモックに変更](#3-Reactを意識したモックに変更)
[4.Todoの入力機能](#4-Todoの入力機能)
[5.Todoの追加機能](#5-Todoの追加機能)
[6.Todoの削除機能](#6-Todoの削除機能)
[7.Todoの完了機能](#7-Todoの完了機能)
[8.Todoの戻す機能](#8-Todoの戻す機能)
[9.コンポーネント化](#9-コンポーネント化)

## 1. create-react-app
環境構築が終了したらターミナルで`create-react-app`を実行しましょう。

```php:ターミナル
projects $ npx create-react-app 任意のフォルダ名（今回はreact-todo）
projects $ cd react-todo

// サーバー立ち上げ
react-todo $ npm start
```
ブラウザが立ち上がってReactの初期画面が表示されれば成功です！

## 2. HTMLとCSSで構造を作成
まずは、イメージの通りに骨組みを作成します。
srcディレクトリの必要なファイルは以下通りです。
`App.js`はコンポーネントと明示的にわかるように`App.jsx`と拡張子を変更しましょう。

```php:
src
|-App.jsx
|-index.js
|-App.css
```
次に`index.js`を編集します。

```php:index.js
import React from 'react';
import ReactDom  from 'react-dom';
import { App } from './App';


ReactDom.render(<App />, document.getElementById('root'));
```
上から2行はおまじないです。
3行目は`App.jsx`のAppという関数をimportしますという意味です。
最後にrender関数でAppの中身をindex.htmlのidがrootのdivタグに格納しています。

準備が整ったのでメインのApp.jsxを編集します。

```php:App.jsx
import React from 'react';
// App.cssを読み込み
import './App.css';

export const App = () => {
  return (
    <>
      <div className="outline">
        <div className="inputArea">
          <input placeholder="todoを入力"/>
          <button>追加</button>
        </div>
        <div className="imcompleteArea">
          <p className="title">未完了のtodo</p>
          <ul>
            <li className="todoList">
              <p>テスト</p>
              <button>完了</button>
              <button>削除</button>
            </li>
          </ul>
        </div>
        <div className="completeArea">
        <p className="title2">完了のtodo</p>
          <ul>
            <li className="todoList">
              <p>テスト</p>
              <button>戻す</button>
            </li>
          </ul>
        </div>
      </div>
    </>
  );
};
```
構成としては大きく`inputArea`、`imcompleteArea`、`completeArea`に分かれます。
Todoが格納させるエリアにはリストタグで個々のtodoにボタンで操作できるようにしておきます。

### ポイント

* **return内が複数行になるときは`<> </>`で全体をくくる**
* **JSXでclassを付与するときはclassName記述する**

次にcssですが、ここはお好みで調整可能です。
記述方法は通常のcssとかわりません。

```php:App.css
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

li {
  list-style: none;
}

.outline {
  padding: 50px;
}

.inputArea {
  width: 300px;
  height: 70px;
  background-color: lightblue;
  padding: 20px;
  margin-bottom: 50px;
  border-radius: 15px;
}
input {
  border-radius: 16px;
  border: none;
  outline: none;
  padding: 6px 16px;
  margin-right: 10px;
}

button {
  border-radius: 16px;
  border: none;
  padding: 4px 16px;
}
button:hover {
  background-color: #FF7FFF;
  color: #FFF;
  cursor: pointer;
}

/* 未完了のタスクエリア */
.imcompleteArea {
  width: 800px; 
  min-height: 300px;
  background-color: lightgray;
  padding: 20px;
  margin-bottom: 50px;
  border-radius: 15px;
}
.title {
  text-align: center;
  font-weight: bold;
}
.todoList {
  margin-top: 20px;
  display: flex;
  align-items: center;
  justify-content: space-evenly;
}

/* 完了のタスクエリア */
.completeArea {
  width: 800px;
  min-height: 300px;
  background-color: lightgreen;
  padding: 20px;
  border-radius: 15px;

}
.title2 {
  text-align: center;
  font-weight: bold;  
}
```
以上で骨組みはできました。

## 3. Reactを意識したモックに変更
現状のApp.jsx内では未完了と完了のtodoはpタグに直接書き込まれています。
しかし、本来は動的にtodoの内容は反映してほしいので`useState`を用いて状態を管理していきたいと思います。

```php:App.jsx
// Reactの部分を{ useState }に編集
import { useState } from 'react';
import './App.css';

export const App = () => {
  const [incompleteTodos, setIncompleteTodos] = useState(["宿題", "洗濯"]);

　return (
  :
  :
  );
};
```
`const [incompleteTodos, setIncompleteTodos]`
未完了のtodoの状態を管理するための配列です。`incompleteTodos`が初期値。`setIncompleteTodos`が更新するための変数です。

`useState(["宿題", "洗濯"])`は初期値のincompleteTodosに宿題と洗濯という２つの値を渡しています。
次にreturn内を編集していきます。まずは未完了のtodoからです。

```php:App.jsx

return (
  :
  :
   <div className="imcompleteArea">
     <p className="title">未完了のtodo</p>
       <ul>
         <li className="todoList">
           <p>テスト</p>
           <button>完了</button>
           <button>削除</button>
         </li>
       </ul>
    </div>
  :
  :
);

↓　// 変更
<div className="imcompleteArea">
  <p className="title">未完了のtodo</p>
    <ul>
      {incompleteTodos.map((todo) => {
         return (
           <li key={todo} className="todoList">
             <p>{todo}</p>
             <button>完了</button>
             <button>削除</button>
           </li>
         );
        })}
    </ul>
</div>
```
順番に解説していきます。
まず`map関数`を用いて先程定義した`incompleteTodos`の中身を繰り返し処理で表示させます。その際の引数は**todo**とします。

`key={todo}`と記述したのは繰り返し処理によるレンダリングでどのtodoかを判別するためです。最後にpタグの中身は`{ }`でtodoを囲んで表示させます。

### ポイント

* **return内でJavascriptを扱う際は{ }で囲む**

同じようにして完了のtodoもuseStateを用いて状態を管理します。
`completeArea`を以下のように編集してください。

``` php:App.jsx
export const App = () => {
  const [incompleteTodos, setIncompleteTodos] = useState(["宿題", "洗濯"]);
  // 追記
  const [completeTodos, setCompleteTodos] = useState(["掃除"]);
　
　return (
    :
    :
    <div className="completeArea">
      <p className="title2">完了のtodo</p>
      <ul>
        {completeTodos.map((todo) => {
          return (
            <li key={todo} className="todoList">
              <p>{todo}</p>
              <button>戻す</button>
            </li>
          );
        })}
      </ul>
   </div>
  );
};
```
これで準備が整いました!
それでは順番に機能を実装していきたいと思います。

## 4. Todoの入力機能
まずは、入力されたtodoの状態を管理するためのuseStateを用意します。
初期値は`''`で空にしておきましょう。

```php:App.jsx
export const App = () => {
　// 追記
  const [todoText, setTodoText] = useState('');
};
```
入力された`todoText`はinputタグのvalueとして設定します。
`{ }`で変数を囲むのを忘れないでください。

```php:
<div className="inputArea">
  <input placeholder="todoを入力" value={todoText} />
  <button>追加</button>
</div>
```
この状態だと入力欄にテキストを打ち込んでも反応しません。それは初期値の`''`が常に渡されているためです。
そこで`onChangeメソッド`を用いて`todoText`の値を更新する関数を作成する必要があります。
inputタグを次のように編集しましょう。

```php:
<div className="inputArea">
  <input placeholder="todoを入力" value={todoText} onChange={onChangeTodoText}/>
  <button>追加</button>
</div>
```
inputにtodoが入力されたら、onChangeメソッドで`onChangeTodoText`という関数を呼び出しています。この関数の中でtodoの値を更新する処理を記述していきます。
では、onChangeTodoText関数を定義します。関数はreturnの前に定義しましょう。

```php:
export const App = () => {
  // 関数の定義
　const onChangeTodoText = (event) => setTodoText(event.target.value);
};
```
onChangeTodoTextは引数を取ります。今回は`event`を取ります。
`event.target.value`は入力されたtodoの値のことです。これをsetTodoText関数の引数として渡すことで入力された値がsetTodoTextとして更新されます。

## 5. Todoの追加機能
4までの内容でtodoを入力することはできました。
次に追加ボタンを押してtodoを未完了エリアに移動させる機能を実装します。
buttonタグに`onClickメソッド`でボタンを押したときに関数を呼び出します。
今回は`onClickAdd`という関数名にします。

```php:
<div className="inputArea">
  <input placeholder="todoを入力" value={todoText} onChange={onChangeTodoText} />
  <button onClick={onClickAdd}>追加</button>
</div>
```
ではonClickAddの中身を記述しましょう。
これもreturnの前に記述します。

```php:
const onClickAdd = () => {
    if (todoText === '') return;
    const newTodos = [...incompleteTodos, todoText];
    setIncompleteTodos(newTodos);
    setTodoText('');
}
```
2行目のifは空の入力は無効にするという意味です。

3行目の`[...incompleteTodos, todoText]`は配列のコピーをしています。
`[...変数名]`と記述するとその変数の値をコピーできます。すでにある未完了のtodoに新しく入力した`todoText`を加えて、新しく`newTodos`という変数に格納しています。

4行目は未完了のtodoのstateを更新したいのでnewTodosを`setIncompleteTodos関数`の引数に渡して値を更新します。

5行目は追加ボタンを押した際に入力欄をリセットしたいので`setTodoText関数`を空にします。

## 6. Todoの削除機能
未完了エリアのtodoを削除する機能を実装します。方針は次の通りです。

**1. 削除ボタンにonClickメソッドを設定して関数を呼び出す**
**2. 削除ボタンを押したら未完了エリアから選んだtodoが削除される**

### 削除ボタンにonClickメソッドを設定
todoを削除する関数を`onClickDelete`とします。

```php:
 <div className="imcompleteArea">
   <p className="title">未完了のtodo</p>
     <ul>
       {incompleteTodos.map((todo, index) => {
         return (
           <li key={todo} className="todoList">
             <p>{todo}</p>
             <button }>完了</button>
             <button onClick={() => onClickDelete(index)>削除</button>
           </li>
         );
       })}
    </ul>
 </div>
```
ポイントとしてはmap関数に新しく`index`を引数に設定しています。
これはtodoを削除する際に何番目のtodoかを判別する際に必要です。関数の中でもindexは必要なのでonClickDeleteの引数にもindexを渡します。

### 関数の定義
例によってreturnの前に記述しましょう。

```php:
const onClickDelete = (index) => {
    const newTodos = [...incompleteTodos];
    // newTodosから指定のindex番号を１つ削除する
    newTodos.splice(index, 1);
    setIncompleteTodos(newTodos);
};
```
2行目で現在の未完了のtodoを`newTodos`という変数に格納します。

3行目で`spliceメソッド`を使用しています。spliceは配列の要素を操作できるメソッドで要素の追加や削除することができます。第一引数に削除対象のindexを渡して第二引数は指定したindexから何番目までの要素を操作するかを設定します。(今回は1なのでつまり、指定したtodoのみを削除します。)

4行目で削除し終えた未完了のtodoの状態を更新するために`setIncompleteTodos関数`にnewTodosを渡して値を更新します。

## 7. Todoの完了機能
未完了のtodoを完了エリアに移動させます。方針は以下の通りです。

**1. 完了ボタンにonClickメソッドを設定して関数を呼び出す**
**2. 完了ボタンを押したら未完了エリアからtodoが削除される**
**3. 完了ボタンを押したら削除されたtodoが完了エリアに追加される**

### 完了ボタンにonClickメソッドを設定
関数名は`onClickComplete`とします。

```php:
<div className="imcompleteArea">
  <p className="title">未完了のtodo</p>
    <ul>
      {incompleteTodos.map((todo, index) => {
        return (
          <li key={todo} className="todoList">
            <p>{todo}</p>
            <button onClick={() => onClickComplete(index)}>完了</button>
            <button onClick={() => onClickDelete(index)}>削除</button>
          </li>
         );
       })}
    </ul>
</div>
```
`onClickComplete`にも、どのtodoを完了にするか判別するために引数としてindexを渡しましょう。

### 関数を定義

```php:
  const onClickComplete = (index) => {
    const newIncompleteTodos = [...incompleteTodos];
    // newTodosから指定のindex番号を１つ削除する
    newIncompleteTodos.splice(index, 1);

    const newCompleteTodos = [...completeTodos, incompleteTodos[index]];
    setIncompleteTodos(newIncompleteTodos);
    setCompleteTodos(newCompleteTodos);
  };
```
2~4行目で未完了エリアから選んだtodoを削除しています。`[...incompleteTodos]`で現在の未完了のtodoをコピーして、変数`newIncompleteTodos`に格納します。次にspliceメソッドでtodoを削除します。

6~8行目で選んだ未完了エリアと完了エリアにtodoを更新してます。
`[...completeTodos, incompleteTodos[index]]`で完了エリアに現在の完了のtodoである`completeTodos`をコピーし、選んだ未完了のtodoである`incompleteTodos[index]`を追加し
変数`newCompleteTodos`に格納します。

最後に未完了エリアのtodoの値と完了エリアのtodoの値をuseStateで更新しています。

## 8. Todoの戻す機能
機能としてはこれで最後です。方針は以下の通りです。

**1. 戻すボタンにonClickメソッドを設定し関数を呼び出す**
**2. 戻すボタンを押したら完了エリアから選んだtodoを削除する**
**3. 戻すボタンを押したら未完了エリアに選んだtodoを追加する**

完了機能の逆ですね

### 戻すボタンにonClickメソッドを設定
関数名は`onClickBack`とします。

```php:
 <div className="completeArea">
   <p className="title2">完了のtodo</p>
     <ul>
       {completeTodos.map((todo, index) => {
         return (
           <li key={todo} className="todoList">
             <p>{todo}</p>
             <button onClick={() => onClickBack(index)}>戻す</button>
           </li>
         );
        })}
     </ul>
 </div>
```
例によってmap関数とonClickBack関数にindexを引数として渡します。

### 関数を定義

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

  const newIncompleteTodos = [...incompleteTodos, completeTodos[index]];
  setCompleteTodos(newCompleteTodos);
  setIncompleteTodos(newIncompleteTodos);
};
```
2~3行目で完了エリアから選んだtodoを削除します。

5~7行目は完了機能の逆なので説明は割愛します。

これで一応機能は実装しました。最終的なコードはこんな感じです。

```php:App.jsx
import { useState } from 'react';
import './App.css';

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から指定のindex番号を１つ削除する
    newTodos.splice(index, 1);
    setIncompleteTodos(newTodos);
  };

  // 未完了のタスクを完了タスクへ
  const onClickComplete = (index) => {
    const newIncompleteTodos = [...incompleteTodos];
    // newTodosから指定のindex番号を１つ削除する
    newIncompleteTodos.splice(index, 1);

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

  // 完了のタスクを未完了のタスクへ
  const onClickBack = (index) => {
    const newCompleteTodos = [...completeTodos];
    newCompleteTodos.splice(index, 1);

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

  return (
    <>
      <div className="outline">
        <div className="inputArea">
          <input placeholder="todoを入力" value={todoText} onChange={onChangeTodoText} />
          <button onClick={onClickAdd}>追加</button>
        </div>
        <div className="imcompleteArea">
          <p className="title">未完了のtodo</p>
          <ul>
            {incompleteTodos.map((todo, index) => {
              return (
                <li key={todo} className="todoList">
                  <p>{todo}</p>
                  <button onClick={() => onClickComplete(index)}>完了</button>
                  <button onClick={() => onClickDelete(index)}>削除</button>
                </li>
              );
            })}
          </ul>
        </div>
        <div className="completeArea">
          <p className="title2">完了のtodo</p>
          <ul>
            {completeTodos.map((todo, index) => {
              return (
                <li key={todo} className="todoList">
                  <p>{todo}</p>
                  <button onClick={() => onClickBack(index)}>戻す</button>
                </li>
              );
            })}
          </ul>
        </div>
      </div>
    </>
  );
};
```
それでは、ここからコンポーネント化をしてコードをスッキリさせたいと思います。

## 9. コンポーネント化
今回はインプットエリア、未完了エリア、完了エリアの3つにコンポーネント化していきます。
まずはインプットエリアから始めます。src配下に`components`ディレクトリを作成し、`InputTodo.jsx`というファイルを作成します。

```php:
src
|-components
      |-InputTodo.jsx
|-index.js
|-App.jsx
|-App.css
```

### inputAreaのコンポーネント化
それではInputTodo.jsxに必要な記述をしていきます。
App.jsxのreturn内の`inputArea`の中身をInputTodo.jsxのreturn内に切り抜きします。
また、App.jsxでこのファイルを使用できるようにexportします。

```php:InputTodo.jsx
import React from 'react';

export const InputTodo = () => {
  return (
    <div className="inputArea">
      <input placeholder="todoを入力" value={todoText} onChange={onChangeTodoText} />
      <button onClick={onClickAdd}>追加</button>
    </div>
  );
};
```

次にApp.jsxを編集します。
InputTodo.jsxをimportします。

```App.jsx
import { useState } from 'react';
import './App.css';
// 追記
import { InputTodo } from './components/InputTodo';
:
:
return (
  <div className="outline">
    <InputTodo />
    <div className="imcompleteArea">
    :
    :
);
```
このままではInputTodo.jsx内に`todoText`や`onChangeTodoText`などの変数や関数が設定されてないのでエラーが起きてしまいます。なので**props**を用いてInputTodo.jsxに必要な要素を渡します。

```php:App.jsx
:
:
return (
  :
  :
  <InputTodo 
    todoText={todoText} 
    onChange={onChangeTodoText} 
    onClick={onClickAdd} 
  />
);
```
InputTodoに３つのpropsを設定します。
それぞれ`todoText`、`onChange`、`onClick`とします。

```php:InputTodo.jsx
import React from 'react';

export const InputTodo = (props) => {
  const { todoText, onChange, onClick } = props;
  return (
    <div className="inputArea">
    <input 
      placeholder="todoを入力" 
      value={todoText} 
      onChange={onChange} 
    />
    <button onClick={onClick}>追加</button>
  </div>
  );
};
```
propsを引数に取り、それぞれの値として渡します。`const { todoText, onChange, onClick } = props;`は分割代入といってメソッドを書くときに簡略化することができます。例えば本来なら`props.todoText`や`props.onChange`と書かないといけないところを`todoText`や`onChange`と省略できます。
これでインプットエリアのコンポーネント化は完了です。

### imcompleteAreaのコンポーネント化
コンポーネント用のファイルを作成します。components配下に`IncompleteTodos.jsx`を作成します。

```php:
src
|-components
      |-InputTodo.jsx
      |-IncompleteTodos.jsx
|-index.js
|-App.jsx
|-App.css
```
同じようにincompleteAreaの中身を切り取ります。

```php:IncompleteTodos.jsx
import React from 'react';

export const IncompleteTodos = () => {
  return (
    <div className="imcompleteArea">
      <p className="title">未完了のtodo</p>
      <ul>
        {incompleteTodos.map((todo, index) => {
          return (
            <li key={todo} className="todoList">
              <p>{todo}</p>
              <button onClick={() => onClickComplete(index)}>完了</button>
              <button onClick={() => onClickDelete(index)}>削除</button>
            </li>
          );
        })}
      </ul>
    </div>
  );
};
```
次にApp.jsxを編集します。

```php:App.jsx
import { useState } from 'react';
import './App.css';
import { InputTodo } from './components/InputTodo';
//　追記
import { IncompleteTodos } from './components/IncompleteTodos';
:
:
rerun (
  :
  :
   <IncompleteTodos />
  :
);
```
それでは必要なpropsを設定します。

```php:App.jsx
:
:
return (
  :
  <IncompleteTodos 
    todos={incompleteTodos} 
    onClickComplete={onClickComplete} 
    onClickDelete={onClickDelete} 
  />
  :
);
```
IncompleteTodos.jsx内で使用する変数と関数をそれぞれ渡します。

```php:IncompleteTodos.jsx
import React from 'react';

export const IncompleteTodos = (props) => {
  const { todos, onClickComplete, onClickDelete } = props;
  return (
    <div className="imcompleteArea">
      <p className="title">未完了のtodo</p>
      <ul>
　　　　　// incompleteTodosをtodosに変更
        {todos.map((todo, index) => {
          return (
            <li key={todo} className="todoList">
              <p>{todo}</p>
              <button onClick={() => onClickComplete(index)}>完了</button>
              <button onClick={() => onClickDelete(index)}>削除</button>
            </li>
          );
        })}
      </ul>
    </div>
  );
};
```
先程と同じように分割代入でpropsを渡します。
それでは最後に完了エリアのコンポーネント化をしていきます。

### completeAreaのコンポーネント化
コンポーネント用のファイルを作成します。components配下にCompleteTodos.jsxを作成します。

```php:
src
|-components
      |-InputTodo.jsx
      |-IncompleteTodos.jsx
      |-CompleteTodos.jsx
|-index.js
|-App.jsx
|-App.css
```
ここも同様にcompleteAreaの中身を切り取ります。

```php:CompleteTodos.jsx
import React from 'react';

export const CompleteTodos = () => {
  return (
    <div className="completeArea">
      <p className="title2">完了のtodo</p>
      <ul>
        {completeTodos.map((todo, index) => {
          return (
            <li key={todo} className="todoList">
              <p>{todo}</p>
              <button onClick={() => onClickBack(index)}>戻す</button>
            </li>
          );
        })}
      </ul>
    </div>
  );
};
```
次にApp.jsxを編集します。

```App.jsx
import { useState } from 'react';
import './App.css';
import { InputTodo } from './components/InputTodo';
import { IncompleteTodos } from './components/IncompleteTodos';
// 追記
import { CompleteTodos } from './components/CompleteTodos';

:
return (
  :
  :
  <CompleteTodos />
  :
);
```
それでは必要なpropsを設定します。

```php:App.jsx
:
:
return (
  :
   <CompleteTodos 
     todos={completeTodos} 
     onClickBack={onClickBack} 
   />
);
```
それではpropsを渡しましょう。

```php:CompleteTodos.jsx
import React from 'react';

export const CompleteTodos = (props) => {
  const { todos, onClickBack} = props;
  return (
    <div className="completeArea">
      <p className="title2">完了のtodo</p>
      <ul>
        {todos.map((todo, index) => {
          return (
            <li key={todo} className="todoList">
              <p>{todo}</p>
              <button onClick={() => onClickBack(index)}>戻す</button>
            </li>
          );
        })}
      </ul>
    </div>
  );
};
```
以上でコンポーネント化完了です！

# 終わりに
記事が長くなってしまいましたが、最後まで読んでいただきありがとうございました！
React学習の助けになれば幸いです。。。