初心者の挑戦！ReactのHooksを活用してTodoリストをつくる【ログ表示＆全削除機能編】

フロントエンドエンジニアを目指しReactを学んでいます。

この記事では、何回かに分けてReactのHooksを使ったTodoリスト作成をまとめていきます。目的は学びを整理するためです。

<h2>記事の構成</h2>

記事は、以下のような三部作にする予定です。

<ol>
<li>Todoリストの登録機能と削除機能を実装する</li>
<li>localStrogeを使った永続化と編集機能を実装する</li>
<li>ログを実装する</li>
</ol>

①<a src="https://qiita.com/tom_works/items/d80904f65e9ce63e7c08">初心者の挑戦！ReactのHooksを活用してTodoリストをつくる【投稿＆削除機能編】</a>

②<a src="https://qiita.com/tom_works/items/53a46545d601e5f29057">初心者の挑戦！ReactのHooksを活用してTodoリストをつくる【編集機能＆永続化編】</a>


上記の記事でそれぞれ実装していますので読んでみてください。

こちらの記事では「③ ログを実装する」を行います。当初の予定にはなかったですがついでに全削除ボタンも作ってみました。

<h2>記事をおすすめする人</h2>

・プログラミング初心者でReactに興味がある人
・ReactのTodoリスト作成で躓いている人
・ReactのHooksについて学んでいる人


<h2>記事を読む上での注意点</h2>

デザインにはTailwind CSSを使用していますが、ReactやTailwind CSSの設定方法などにも触れません。

コードがまだまだ未熟ですので、アドバイスいただけると幸いです。

<h2>さっそく実装スタート</h2>

今回行う実装処理は、細かく分けると以下になります。

<ul>
<li>インプットした日時が表示されるようにする</li>
<li>編集したら日時も変更されるようにする</li>
</ul>

まずは、インプットしたら日時が表示されるようにしていきます。

```js
const handleOnSubmit = () => {
   const newTodo = {
     id: Math.floor(Math.random() * 1000),
     value: input,
     time: new Date().toLocaleString(),
   };
   if (input !== "") setTodoLists([...todoLists, newTodo]);
 
   setInput("");
 };
```
日時を表示するために、handlOnSubmit関数の中でtimeを定義し、new Date().toLocaleString()というメソッドを活用しました。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString

![スクリーンショット 2022-03-14 20.48.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525801/73d8974e-b50a-3570-2013-db80adf92d73.png)

これでタスク一覧に日時が表示できました！

ただ、このままだと編集しても同じ日時になってしまうので、編集したときは編集時の日時に更新されるようにします。

```js
const submitEdits = (id) => {
   const updatedTodoLists = [...todoLists].map((todoList) => {
     if (todoList.id === id) {
       todoList.value = editingText;
       todoList.time = new Date().toLocaleString();
     }
     return todoList;
   });
   setTodoLists(updatedTodoLists);
   setTodoEditing(null);
   setEditingText("");
 };
```
上記のように、submitEditsの関数内で todoList.time に改めて new Date(). toLocalString(); を代入します。

![スクリーンショット 2022-03-14 20.53.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525801/1a852ccb-cf9c-0cc5-1510-ae02c7bb5ebd.png)

これで編集したときの日時が更新できるようになりました！

<h2>ついでに全削除ボタンも作ってみる</h2>

タスクが増えてくるとひとつひとつ消すのが面倒なので一気に消せるように全削除ボタンを作りたいと思います。

以下の手順で実装していきます。

<ul>
<li>誤ってすべて消さないよう、ボタンを押したら事前に確認のフォームを出す</li>
<li>リストが空のときは機能しないようにする</li>
<li>すべて削除できるようにする（＝クリックしたら配列が空になるようにする）</li>
</ul>

```js
  <button
           type="submit"
           className="bg-indigo-700 font-semibold text-white py-2 px-4 rounded m-6 w-3/4 container mx-auto"
         >
           追加
         </button>
         <button
           onClick={handleAllDelete}
           disabled={todoLists.length === 0}
           className="bg-red-700 font-semibold text-white py-2 px-4 rounded m-2 w-3/4 container mx-auto"
         >
           すべて削除
         </button>
```
まずは追加ボタンの下に「すべて削除」ボタンを作ります。

<button>の中に onClick {handleAllDelete}というイベントハンドラを指定します。

また、リスト（todoLists）が空のときは、ボタンを押せないようにするために、disabled = { todoLists.length === 0  } を指定。「todoLists内の長さがゼロなら（配列がゼロなら）ボタンを機能できなくしなさい（disabled）」と命令します。

![スクリーンショット 2022-03-14 21.14.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525801/55a4f7c2-8839-0b44-ed33-7864585eb9c9.png)

これでボタンが完成し、リストが空のときはボタンが機能できないようにできました！

続いて、handleAllDeleteの関数を定義し、全て削除するための処理を書いていきます。

```js
const handleAllDelete = () => {
   const confirm = window.confirm("本当にすべてを削除しますか");
   if (confirm) return setTodoLists([]);
 };
```

まず、アクシデントで全消去をしてしまうことを防ぐために window.confirmメソッドを活用して確認フォームが出るようにしてあげます。

そして、if (confirm ) return setTodoLists([]) とすることで「もし確認フォームの返答がOKなら、setTodoLists([])で配列が空になるようにしなさい」という命令を出します。

![スクリーンショット 2022-03-14 21.00.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525801/359db3d8-1e08-6875-c731-a61d845fb499.png)


これで、全て削除するまえに確認フォームが出るようになり、OKを押すことですべて消せるようになりました！

<h2>現時点でのコード</h2>

```js
import React, { useState, useEffect } from "react";
 
const App = () => {
 const [input, setInput] = useState("");
 const [todoLists, setTodoLists] = useState([]);
 const [todoEditing, setTodoEditing] = useState(null);
 const [editingText, setEditingText] = useState("");
 
 useEffect(() => {
   const temp = localStorage.getItem("keepTodo");
   const loadedTodo = JSON.parse(temp);
   if (loadedTodo) {
     setTodoLists(loadedTodo);
   }
 }, []);
 
 useEffect(() => {
   const temp = JSON.stringify(todoLists);
   localStorage.setItem("keepTodo", temp);
 }, [todoLists]);
 
 const handleOnSubmit = () => {
   const newTodo = {
     id: Math.floor(Math.random() * 1000),
     value: input,
     time: new Date().toLocaleString(),
   };
   if (input !== "") setTodoLists([...todoLists, newTodo]);
 
   setInput("");
 };
 
 const handleAllDelete = () => {
   const confirm = window.confirm("本当にすべてを削除しますか");
   if (confirm) return setTodoLists([]);
 };
 
 const addTodo = (e) => {
   e.preventDefault();
   handleOnSubmit();
 };
 
 const deleteListButton = (id) => {
   const newArray = todoLists.filter((todoLists) => todoLists.id !== id);
   setTodoLists(newArray);
 };
 
 const submitEdits = (id) => {
   const updatedTodoLists = [...todoLists].map((todoList) => {
     if (todoList.id === id) {
       todoList.value = editingText;
       todoList.time = new Date().toLocaleString();
     }
     return todoList;
   });
   setTodoLists(updatedTodoLists);
   setTodoEditing(null);
   setEditingText("");
 };
 
 return (
   <>
     <h1 className="text-center text-3xl font-bold mt-10">
       Todo List Practice
     </h1>
     <div className="text-center mt-7">
       <form onSubmit={addTodo}>
         <input
           type="text"
           value={input}
           onChange={(e) => setInput(e.target.value)}
           placeholder="タスクを入力"
           className=" p-3 w-4/5 border-2"
         />
         <button
           type="submit"
           className="bg-indigo-700 font-semibold text-white py-2 px-4 rounded m-6 w-3/4 container mx-auto"
         >
           追加
         </button>
         <button
           onClick={handleAllDelete}
           disabled={todoLists.length === 0}
           className="bg-red-700 font-semibold text-white py-2 px-4 rounded m-2 w-3/4 container mx-auto"
         >
           すべて削除
         </button>
       </form>
       <h3 className="text-xl font-bold text-center my-10">Todo一覧</h3>
 
       {/* 以降map開始 */}
 
       <div className="mt-3 container mx-auto">
         {todoLists.map((todoList) => {
           return (
             <>
               {todoEditing === todoList.id ? (
                 <input
                   type="text"
                   placeholder="編集内容を入力"
                   className="m-7 p-3 w-4/5 border-2"
                   value={editingText}
                   onChange={(e) => setEditingText(e.target.value)}
                 />
               ) : (
                 <div>{""}</div>
               )}
 
               <ul className="flex justify-between mx-20 my-2  ">
                 <li key={todoList.id}>{todoList.id}</li>
                 <li>{todoList.value}</li>
                 <li>{todoList.time}</li>
                 <li>
                   <button onClick={() => deleteListButton(todoList.id)}>
                     削除
                   </button>
 
                   {todoList.id === todoEditing ? (
                     <button
                       onClick={() => submitEdits(todoList.id)}
                       disabled={false}
                       className="ml-7"
                     >
                       再投稿
                     </button>
                   ) : (
                     <button
                       onClick={() => setTodoEditing(todoList.id)}
                       className="ml-7"
                     >
                       編集
                     </button>
                   )}
                 </li>
               </ul>
               <div className="mx-auto border-b-2 border-gray-200 w-5/6 my-4"></div>
             </>
           );
         })}
       </div>
     </div>
   </>
 );
};
 
export default App;
```
<h2>まとめ</h2>

今回は、ログを表示するための実装と全削除ボタン機能を実装しました。

実はまだ課題があって、いまだに編集の入力フォームが空のままでも入力できてしまいます。ここがなかなか解決できずにいます。

そこで、また別に続編記事を作り以下のことをやっていきたいなと思っています。

<ul>
<li>編集フォームが空のとき入力ができないようにする</li>
<li>フィルター機能を追加し、完了と未完了で表示を変える</li>
</ul>

次回も頑張ります。