ReactとかNextを使うことになったから頑張る③

# はじめに
①の記事：https://qiita.com/atsu123456789/items/c055959b288f53841324
②の記事：https://qiita.com/atsu123456789/items/9fc5b6bf02dafd9e0212

参考にしているもの↓

https://www.udemy.com/course/modern_javascipt_react_beginner/?utm_source=adwords&utm_medium=udemyads&utm_campaign=LongTail_la.JA_cc.JP&utm_content=deal4584&utm_term=_._ag_107181210924_._ad_452531407122_._kw__._de_c_._dm__._pl__._ti_dsa-930814701079_._li_9165953_._pd__._&matchtype=&gclid=CjwKCAjwscGjBhAXEiwAswQqNGeULoH_ng9LYQJoKL6c3fLyaTexZ2SemCewOsmc54SIsbI0PvZLQRoCz6EQAvD_BwE


今回の記事ではReactのみを使ったTodoアプリの開発についてです。
バックエンドやDBなどは使っていないので、データの保存等はできませんが、Reactをどう動かしていくのかのイメージが大分つくのではないでしょうか。

# 対象者
- 基本的なプログラミングの考え方が分かる(メソッドなど)
- 最低限のJavascriptが分かる(自信ない方は[①の記事](https://qiita.com/atsu123456789/items/c055959b288f53841324)へGo)
- 最低限のReactの書き方が分かる(自信ない方は[②の記事](https://qiita.com/atsu123456789/items/9fc5b6bf02dafd9e0212)へGo)
- ReactやNext.jsに興味がある
- 最低限のHTML、CSSが分かる

# ひな形の作成
``` index.js
import React from 'react';
import ReactDom from 'react-dom'
import { App } from './App';

ReactDom.render(<App />, document.getElementById("root"));
```

``` App.jsx
import React from "react"
import './App.css';

export const App = () => {
  return (
    <>
      <div className="input-area">
        <input placeholder='TODOを入力' />
        <button>追加</button>
      </div>
      <div className="incomplete-area">
        <p className="title">未完了のTODO</p>
        <ul>
          <div className="list-row">
            <li>ああああ</li>
            <button>完了</button>
            <button>削除</button>
          </div>
          <div className="list-row">
            <li>いいいい</li>
            <button>完了</button>
            <button>削除</button>
          </div>
        </ul>
      </div>
      <div className="complete-area">
        <p className="title">完了のTODO</p>
        <ul>
          <div className="list-row">
            <li>うううう</li>
            <button>戻す</button>
          </div>
        </ul>
      </div>
    </>
  );
};
```

CSSは適当に設定しています。
するとこんな感じ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1193085/35c167fb-a43b-6f89-d3ea-9f1d605e379f.png)

## 未完了のTODOのコンポーネント化
useStateを使って
``` jsx
const [incompleteTodos, setIncompleteTodos] = useState(['ああああ', 'いいいい']);
```

こうしてmap関数を用いることで
``` App.jsx
<div className="incomplete-area">
        <p className="title">未完了のTODO</p>
        <ul>
          { incompleteTodos.map((todo) => {
            return (
              <div key={todo} className="list-row">
                <li>{todo}</li>
                <button>完了</button>
                <button>削除</button>
              </div>
            )
          }) }
        </ul>
      </div>
```

このように書き換えることが出来る。
ここでreturn内のdivタグにkeyを設定する。
これはレンダリングする際にkeyの変化のみを書き換えてねーっていうことを教えてあげるものらしい

## 完了のTODOのコンポーネント化
先ほどと同様にして完了のTODOも書き換える
``` jsx
const [completeTodos, setCompleteTodos] = useState(['うううう']);
```

``` App.jsx
<div className="complete-area">
        <p className="title">完了のTODO</p>
        <ul>
          {completeTodos.map((todo) => {
            return (
              <div key={todo} className="list-row">
                <li>{ todo }</li>
                <button>戻す</button>
              </div>
            );
          })}
        </ul>
      </div>
```

# 追加ボタン押下時の処理
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1193085/5494d139-92a0-c429-e9fc-d6917e0b578a.png)
こいつを押したときに未完了のTODOに内容を追加する。

まずApp.jsxの内容を書き換える
``` App.jsx
<div className="input-area">
    <input
      placeholder='TODOを入力'
      value={todoText}
      onChange={onChangeTodoText}
    />
    <button onClick={onClickAdd}>追加</button>
</div>
```
これでinputタグ内に文字を入力すると、onChangeTextという関数が、buttonクリック時にはonClickAddという関数が働く。

ではそれぞれの関数の処理を書いていく。

``` App.jsx
  const onChangeTodoText = (e) => setTodoText(e.target.value);

  const onClickAdd = () => {
    if (todoText === "") return;
    const newTodos = [...incompleteTodos, todoText];
    setIncompleteTodos(newTodos);
    setTodoText("");
  };
```
onChangeTodoTextでは、inputタグの変化に応じてその値を取得し、toDoTextというStateで定義した値の中に入れる。

onClickAddでは上で指定したtodoTextが空でない場合に限り、incompleteTodos配列にtodoTextを追加し、新たなリストとして、Stateで定義したincompleteTodosに格納している。

こうすることで追加ボタン押下時に未完了のTODOに値が入るようになった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1193085/4579ef29-f93e-69f4-c6d9-6a6604d5fb94.png)

# 削除の機能

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1193085/7d5f9928-3ef7-033d-b618-acd4d7763f3d.png)

次はこの削除ボタン押下時に未完了のTODOから要素が削除されるように実装していく。

まずは削除ボタン押下時に処理が走ってほしいので
``` App.jsx
<ul>
          { incompleteTodos.map((todo, index) => {
            return (
              <div key={todo} className="list-row">
                <li>{todo}</li>
                <button>完了</button>
                <button onClick={() => onClickDelete(index)}>削除</button>
              </div>
            )
          }) }
        </ul>
```

onClickDeleteという関数を与える。
この時、何番目の要素を削除するかをわからせるためにmap関数でindexをとり、それをonClickDeleteに渡している。
※Reactでは関数に引数を渡したいとき、
``` jsx
<button onClick={() => onClickDelete(index)}>削除</button>
```
このように、アロー関数の形で書く必要がある。

では実際にonClickDeleteの処理を書いていく。

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

やっていることは至極単純で、incompleteTodosからspliceを用いてindex番号目の要素を削除し、その配列をincompleteTodosに渡しているだけである。

これで削除機能の実装ができた。

# 完了機能の実装

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1193085/55dd6a6f-8583-32ab-d1ee-fa266b9c5aaa.png)

次に完了ボタン押下時に、未完了のTODOから完了のTODOへ移行させる処理を書く。

削除の時と同様、完了ボタン押下時に処理を走らせたいので
``` App.jsx
<ul>
  { incompleteTodos.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>
```

完了ボタン押下時にonClickCompleteという関数を呼び出す。
ではonClickCompleteの処理を書いていく。

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

    const newCompleteTodos = [...completeTodos, incompleteTodos[index]];
    setIncompleteTodos(newIncompleteTodos);
    setCompleteTodos(newCompleteTodos);
  };
```
ここも先ほどの削除と似たような処理を行っており、やっていることとしては
①incompleteTodosからnewIncompleteTodosを作成
②newIncompleteTodosのindex番目の要素を削除
③completeTodosとincompleteTodosのindex番目の要素を連結した配列をnewCompleteTodosとして定義
④incompleteTodos、completeTodosの中身を更新

これで完了ボタンの実装ができた。

# 戻るボタンの実装

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1193085/a4a8abb4-90f9-4cf6-0bef-b8ef8b7b9c10.png)

戻すボタン押下時に完了のTODOから未完了のTODOに移行するように実装していく。

中身的には先ほどの完了の処理とほとんど同じである。

``` App.jsx
<ul>
  {completeTodos.map((todo, index) => {
    return (
      <div key={todo} className="list-row">
        <li>{ todo }</li>
        <button onClick={() => onClickBack(index)}>戻す</button>
      </div>
    );
  })}
</ul>
```
これでbutton押下時にonClickBackという関数が走るので、その関数を実装する

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

    const newIncompleteTodos = [...incompleteTodos, completeTodos[index]];
    setCompleteTodos(newCompleteTodos);
    setIncompleteTodos(newIncompleteTodos);
  };
```
こちらは先ほどの完了ボタンとほぼ逆のことをしているだけであり、
①completeTodosからnewCompleteTodosを作成
②newCompleteTodosのindex番目の要素を削除
③incompleteTodosとcompleteTodosのindex番目の要素を連結した配列をnewIncompleteTodosとして定義
④completeTodos、incompleteTodosの中身を更新

これで戻る機能の実装ができた。

↓App.jsxの最終形
``` App.jsx
import React from "react"
import { useState } from "react";
import './App.css';

export const App = () => {
  const [todoText, setTodoText] = useState('');
  const [incompleteTodos, setIncompleteTodos] = useState([]);
  const [completeTodos, setCompleteTodos] = useState([]);

  const onChangeTodoText = (e) => setTodoText(e.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);

    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="input-area">
        <input
          placeholder='TODOを入力'
          value={todoText}
          onChange={onChangeTodoText}
        />
        <button onClick={onClickAdd}>追加</button>
      </div>
      <div className="incomplete-area">
        <p className="title">未完了のTODO</p>
        <ul>
          { incompleteTodos.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>
      <div className="complete-area">
        <p className="title">完了のTODO</p>
        <ul>
          {completeTodos.map((todo, index) => {
            return (
              <div key={todo} className="list-row">
                <li>{ todo }</li>
                <button onClick={() => onClickBack(index)}>戻す</button>
              </div>
            );
          })}
        </ul>
      </div>
    </>
  );
};

```

# コンポーネント化
ここまで進めると、とりあえず動くTodoアプリができた。
ただApp.jsxをみると、長いファイルとなっており、どこに何の処理をしているかが分かりにくい。
そこでコンポーネントの分割を行っていく。

``` ./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>
	);
};
```

``` App.jsx
{/* ファイルのよびだしを忘れずに！ */}
import { InputTodo } from './components/InputTodo'


{/* 40行目くらい */}
<InputTodo todoText={todoText} onChange={onChangeTodoText} onClick={onClickAdd} />
      <div className="incomplete-area">
        <p className="title">未完了のTODO</p>
```
./components/InputTodo.jsxは先ほどのApp.jsxのinputの部分を別のファイルに書き換えたものである。

App.jsxでInputTodoを呼び出し、引数としてtodoText、onChange、onClickを渡し、これを./components/InputTodo.jsxの中でpropsから分割代入することで値を取得している。

どうように他の要素もコンポーネント化すると先ほどの最終形が以下のようになる
``` App.jsx
import React from "react"
import { useState } from "react";
import './App.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 = (e) => setTodoText(e.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);

    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 (
    <>
      <InputTodo
        todoText={todoText}
        onChange={onChangeTodoText}
        onClick={onClickAdd}
      />
      <IncompleteTodos
        todos={incompleteTodos}
        onClickComplete={onClickComplete}
        onClickDelete={onClickDelete}
      />
      <CompleteTodos
        todos={completeTodos}
        onClickBack={onClickBack}
      />
    </>
  );
};
```
だいぶスッキリ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1193085/35c167fb-a43b-6f89-d3ea-9f1d605e379f.png)

最終形はこんな感じで追加や完了などのボタンを押すと動きます。Reactってすごいね

# 最後に
ここまで読んでくれてありがとうございます。
とりあえずここまでで自分自身、なんとなくReactが見えてきたのでもう少し使い慣れたらNext.jsいじってみたいと思います。
④の記事は当分先になるかも(m´・ω・｀)m ｺﾞﾒﾝ…


# 参考文献

https://www.udemy.com/course/modern_javascipt_react_beginner/?utm_source=adwords&utm_medium=udemyads&utm_campaign=LongTail_la.JA_cc.JP&utm_content=deal4584&utm_term=_._ag_107181210924_._ad_452531407122_._kw__._de_c_._dm__._pl__._ti_dsa-930814701079_._li_9165953_._pd__._&matchtype=&gclid=CjwKCAjwscGjBhAXEiwAswQqNGeULoH_ng9LYQJoKL6c3fLyaTexZ2SemCewOsmc54SIsbI0PvZLQRoCz6EQAvD_BwE