React初心者がHookを使ってTo Doリストを作ったらこうなった

# 目次

* 初めに
* 完成品
* 前提条件
* 各コンポーネントについて
* Appコンポーネント
* Headerコンポーネント
* Formコンポーネント
* ToDoListコンポーネント
* 最終的なコード
* 参考


# 初めに

[React Hooks Advent Calendar 2019 10日目の記事](https://qiita.com/advent-calendar/2019/react-hooks)を書かせていただきました。

まだReactを勉強し始めて間もないですが、やはり**To Doリスト**は一度作っておくべきだろうと思って色々Qiitaを漁っていてあることに気が付きました。

「**React Hookを使って実装している人、ほとんどいないな...**」と（私が見てないだけだと思います）。

そこで私はReact　Hookの一つ、**useState**を使ってTo Doリストを作りました。変数名とか結構ガバガバだと思います。
**React初心者ですがお叱りなどあったら甘んじて受け入れる所存です。よろしくお願いします。**


※[React Hooks Advent Calendar 2019 7日目の@daishiさんの記事](https://qiita.com/daishi/items/8b08b32185a9b050637a)では私とは別のやり方でTo Doリスト作ってますね。

# 完成品

完成品がこちらです。Netlifyでデプロイもしてます。→ [To Do List Used By Hooks](https://to-do-list-used-by-hook.netlify.com)
私はブラウザはなんとなく[Vivaldi](https://vivaldi.com/ja/)使っているのでVivaldiでアクセスした画面になります。

スタイルはほぼ全部`Bootstrap 4`に投げました。

![result.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226452/dad4a4ae-0479-83ea-aeb9-4e425867322c.jpeg)

コードはGitHubに投げました。

https://github.com/koralle/todo-usedby-hook

# 前提条件

* Windows 10
* node.js v12.13.1
* yarn 1.19.2
* Bootstrap 4
* create-react-app 3.2.0

# 各コンポーネントについて

`App`を親コンポーネントにして、画面上から`Header`、`Form`、`ToDoList`で構成しました。

# Appコンポーネント

`return`の中の不自然に見える`<div>`タグは`Bootstrap`であれこれするためにこうなってます...

```jsx:App.js
import React, {useState} from 'react';
import './App.css';
import Header from './Header';
import Form from './Form'
import ToDoList from './ToDoList'

const App = () => {
    // ToDoリストのStateをtoDoListと定義
    const [toDoList, setToDoList] = useState([]);

    // toDoListに項目を追加
    const addToDoList = (Title, Content) => {
        setToDoList(toDoList.concat({"title": Title, "content": Content}));
    }
    // toDoListの項目を削除
    const deleteToDoList = (index) => {
        setToDoList(toDoList.filter(item => toDoList[index] !== item));
    }


    return (
        <div>
            <Header headerTitle="To Do List Used By Hooks"/>

            <div>
                <div>
                    <Form add={addToDoList}/>
                    <ToDoList list={toDoList} delete={deleteToDoList}/>
                </div>
            </div>
        </div>
    );
}

export default App;
```

# Headerコンポーネント

`header`タグをrenderしているだけなので,あまり書くことないです。

```jsx:Header.js
import React from 'react';
import './Header.css'

const Header = (props) => {

    return (
        <header>
            <h1>{props.headerTitle}</h1>
        </header>
    );
}

export default Header;
```

# Formコンポーネント

このコンポーネント、できればステートレスにしたかったんですけど上手くいきませんでした。
Appコンポーネントから`addToDoList()`をpropsとして引き渡してます。

```jsx:Form.js
import React, {useState} from 'react';
import './Form.css'

const Form = (props) => {
    const [toDoTitle, setToDoTitle] = useState("");
    const [toDoContent, setToDoContent] = useState("");

    // Titleフォームの状態の制御
    const handleToDoTitleInputChange = (e) => {
        setToDoTitle(e.target.value);
    }

    // Contentフォームの状態の制御
    const handleToDoContentInputChange = (e) => {
        setToDoContent(e.target.value);
    }

    // 入力フォームのクリア
    const resetInputField = () => {
        setToDoTitle("");
        setToDoContent("");
    }

    // 項目の追加を確定
    const callAddToDoList = (e) => {
        e.preventDefault();
        props.add(toDoTitle,toDoContent);
        resetInputField();
    }

    return (
        <form>
            <div>
                <div>
                    <span>Title</span>
                </div>
                <input type="text"
                    onChange={handleToDoTitleInputChange}
                    value={toDoTitle}
                />
            </div>
            <div>
                <div>
                    <span>Detail</span>
                </div>
                <textarea
                    onChange={handleToDoContentInputChange}
                    value={toDoContent}
                ></textarea>
            </div>
            <div>
                <button type="submit" onClick={callAddToDoList} >
                    ADD
                </button>
            </div>
        </form>
    );
}

export default Form;
```

# ToDoListコンポーネント

AppコンポーネントのStateである`toDoList`と項目を削除する関数`deleteToDoList()`をpropsとして引き渡しています。

```jsx:ToDoList.js
import React from 'react';
import './ToDoList.css'

const ToDoList = (props) => {
    // AppコンポーネントのStateであるtoDoListをpropsとして受け取って
    // mapでループする
    const toDoListItems = props.list.map(
        (item, i) => {
            return (
                <div key={i} >
                    <div>
                        <h5>Title: {item.title}</h5>
                        <p>Content: {item.content}</p>
                        <button onClick={() => props.delete(i)} >
                            Delete
                        </button>
                    </div>
                </div>
            );
        }
    );

    return (
        <div>
            <h1>Your Tasks: {props.list.length}</h1>
            <div>
                {toDoListItems}
            </div>
        </div>
    );
}

export default ToDoList;

```

# 最終的なコード

上のコードに`Bootstrap4`でスタイルを付ける為にclassNameをつけ足していった結果が以下のコードになります。
(CSSファイルは割愛しました。)

```html:./public/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />

    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">

    <title>To Do List used by Hooks</title>
  </head>
  <body>
    <div id="root"></div>

    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
  </body>
</html>

```

```jsx:App.js
import React, {useState} from 'react';
import './App.css';
import Header from './Header';
import Form from './Form'
import ToDoList from './ToDoList'

const App = () => {
    const [toDoList, setToDoList] = useState([]);


    const addToDoList = (Title, Content) => {
        setToDoList(toDoList.concat({"title": Title, "content": Content}));
    }


    const deleteToDoList = (index) => {
        setToDoList(toDoList.filter(item => toDoList[index] !== item));
    }


    return (
        <div className="toDo-app">
            <Header headerTitle="To Do List Used By Hooks"/>

            <div className="toDo-app-body container">
                <div className="toDo-main">
                    <Form add={addToDoList}/>
                    <ToDoList list={toDoList} delete={deleteToDoList}/>
                </div>
            </div>
        </div>
    );
}

export default App;
```


```jsx:Header.js
import React from 'react';
import './Header.css'

const Header = (props) => {

    return (
        <header className="toDo-header">
            <h1>{props.headerTitle}</h1>
        </header>
    );
}

export default Header;
```

```jsx:Form.js
import React, {useState} from 'react';
import './Form.css'

const Form = (props) => {
    const [toDoTitle, setToDoTitle] = useState("");
    const [toDoContent, setToDoContent] = useState("");

    const handleToDoTitleInputChange = (e) => {
        setToDoTitle(e.target.value);
    }

    const handleToDoContentInputChange = (e) => {
        setToDoContent(e.target.value);
    }

    const resetInputField = () => {
        setToDoTitle("");
        setToDoContent("");
    }

    const callAddToDoList = (e) => {
        e.preventDefault();
        props.add(toDoTitle,toDoContent);
        resetInputField();
    }

    return (
        <form className="toDo-form">
            <div className="toDo-form-title input-group"> 
                <div className="input-group-prepend">
                    <span className="input-group-text">Title</span>
                </div>
                <input type="text"
                    className="form-control shadow"
                    onChange={handleToDoTitleInputChange}
                    value={toDoTitle}
                />
            </div>
            <div className="toDo-form-detail input-group">
                <div className="input-group-prepend">
                    <span className="input-group-text">Detail</span>
                </div>
                <textarea className="form-control shadow"
                    aria-label="Detail"
                    onChange={handleToDoContentInputChange}
                    value={toDoContent}
                ></textarea>
            </div>
            <div className="toDo-form-add">
                <button className="btn btn-success"
                    type="submit" onClick={callAddToDoList}
                >
                    ADD
                </button>
            </div>
        </form>
    );
}

export default Form;
```

```jsx:ToDoList.js
import React from 'react';
import './ToDoList.css'

const ToDoList = (props) => {
    const toDoListItems = props.list.map(
        (item, i) => {
            return (
                <div key={i} className="card toDo-item">
                    <div className="card-body">
                        <h5 className="card-title">Title: {item.title}</h5>
                        <p className="card-text">Content: {item.content}</p>
                        <button className="btn btn-danger"
                            onClick={() => props.delete(i)} >
                            Delete
                        </button>
                    </div>
                </div>
            );
        }
    );

    return (
        <div className="toDo-List">
            <h1>Your Tasks: {props.list.length}</h1>
            <div>
                {toDoListItems}
            </div>
        </div>
    );
}

export default ToDoList;
```

# 参考

To Doリストのベースはこちらの記事で学ばせてもらいました。

 * [create-react-appを使ってReactでToDoリストを作成するぞ](https://qiita.com/rioc/items/8723c236e10d989e827d)
 * [ReactでシンプルなTodoアプリケーションを作成する](https://qiita.com/sayama0402/items/6364cef848e2c2d1cf7d)

Formコンポーネントを作る際にかなり参考にしました。
（[2020年のフロントエンドマスターになりたければこの9プロジェクトを作れ](https://qiita.com/rana_kualu/items/915345b8f3f870cfe2aa)で紹介されていました）。

* [How to build a movie search app using React Hooks](https://www.freecodecamp.org/news/how-to-build-a-movie-search-app-using-react-hooks-24eb72ddfaf7/)