LoginSignup
11
9

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-12-09

目次

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

初めに

React Hooks Advent Calendar 2019 10日目の記事を書かせていただきました。

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

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

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

React Hooks Advent Calendar 2019 7日目の@daishiさんの記事では私とは別のやり方でTo Doリスト作ってますね。

完成品

完成品がこちらです。Netlifyでデプロイもしてます。→ To Do List Used By Hooks
私はブラウザはなんとなくVivaldi使っているのでVivaldiでアクセスした画面になります。

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

result.jpg

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

前提条件

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

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

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

Appコンポーネント

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

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しているだけなので,あまり書くことないです。

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として引き渡してます。

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として引き渡しています。

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ファイルは割愛しました。)

./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>

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;
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;
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;
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リストのベースはこちらの記事で学ばせてもらいました。

Formコンポーネントを作る際にかなり参考にしました。
2020年のフロントエンドマスターになりたければこの9プロジェクトを作れで紹介されていました)。

11
9
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
9