フロントエンドエンジニアを目指しReactを学んでいます。
メンターからReactの実装のロジックを理解するためにTodoリストの作成をおすすめされたので、Udemyの講座や先輩エンジニアさんのブログなどを読みインプットとアウトプットを繰り返しています。
この記事では、何回かに分けてReactのHooksを使ったTodoリスト作成をまとめていきます。
目的は主に学びを整理するため。そして、僕と同じようにプログラミングやReactを学び始めたばかりの方に何らかの役に立てば嬉しいです。
記事の構成
記事は、以下のような三部作にする予定です。
- Todoリストの登録機能と削除機能を実装する
- localStrogeを使った永続化と編集機能を実装する
- ログを実装する
第1回目の終了時点では、下の写真のようなシンプルなTodoリストが完成します。
この記事を書いている時点ではまだ道半ばですが、とりあえず上記までは到達したいです。③までいったら次にFirebaseへのデプロイなどもしていきたいと考えています。
記事をおすすめする人
- プログラミング初心者でReactに興味がある人
- ReactのTodoリスト作成で躓いている人
- ReactのHooksについて学んでいる人
Google検索して出てくる記事の中には、Hooksが登場する前の書き方で説明されているものも多かったので、HooksができてからのモダンなReactの実装方法を学びたいという初学者の人にも役立つかなと思います。
記事を読む上での注意点
デザインにはTailwind CSSを使用していますが、そちらの実装については触れません。また、ReactやTailwind CSSの設定方法などにも触れません。
あくまでTodoリストの実装部分に焦点を当てています。
また、僕はまだまだ駆け出しの初級者なので、もっとよくできる部分ございましたらアドバイスいただけると幸いです。
それでは、実装スタート
それではさっそく実装を始めます。
useStateを作る
まずは、App.jsの中にuseStateを使った構文を書いていきます。
import React, { useState } from "react";
const App = () => {
const [input, setInput] = useState("");
const [todoLists, setTodoLists] = useState([]);
useStateは、コンポーネントの状態を管理する役割をします。これでtodo(値)と値を更新するsetTodo(関数)を構築できます。
このときuseStateをreactからインポート(import)することを忘れないようにしましょう。
input タグの作成
<form onSubmit={addTodo}>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="タスクを入力"
className=" p-3 w-4/5 border-2"
/>
次にユーザーが入力した文字を読み取るという状態を作る実装をします。
タグの中でtype(タイプ)文字入力、value(値)useStateで指定した値 {input} 、そして状態に変化を起こすための onChange というイベントハンドラを組み合わせます。
onChangeのなかで展開している{(e) => setTodo(e.target.value)}は、入力された値をsetTodo内で呼び出すというアクションを表す慣用句のようなものです。
どのビデオ講座や記事でも出てきたので暗記でいいと思います。
これでタグの中に文字を入力できる状態になりました!
入力ボタンの作成
<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>
</form>
次に入力した情報を読み込む
とアクションのトリガーとなるを作ります。のtypeを”submit”としタグ内で onSubmitというイベントハンドラを活用します。onSubmitを使って呼び出す関数名は{addTodo}としました。
{}内の文字は自由に指定できますが、直感的に分かりやすいものが良いと思います。また、記載方法はキャメルケースで書くのが慣習とのことです。
続いて、onClickの中で指定した{addTodo}の関数をつくります。
const addTodo = (e) => {
e.preventDefault();
handleOnSubmit();
};
まず、引数にe(event)を渡し、e.preventDefault();を実行します。これは、ページ内でのリロードが起きないようにする処理です。
そして、次にhandleOnSubmit();という別の関数を設置し、呼び出します。
const handleOnSubmit = () => {
const newTodo = {
id: Math.floor(Math.random() * 1000),
value: input,
};
if (input !== "") setTodoLists([...todoLists, newTodo]);
setInput("");
};
上記で書いたhandleOnSubmitの関数を作ります。
今回は、ランダムに出力されるidとinputで入力された内容を表示するように定義し、newTodoという変数に代入しました。
その後、以下の処理をします。
- if (input !== “”) もし空文字で入力されなければ次の処理に入る(=空文字では入力不可)
- useStateで展開したsetTodoList(変化を起こす変数)内で([...todoLists (todoListsを展開し), newTodo(変化後のnewTodoを反映) ] }という処理をする。
- 入力した文字がインプットエリアに残らないように setInput(“”); で初期化する。
タスク表示部分の作成
ここまでで入力した内容をひとつひとつ展開する処理はできるようになったので、次はそれを実際に表示する処理を行っていきます。
処理にはmapメソッドを使います。mapメソッドを使うことで、入力した内容を繰り返し処理できるようになります。
<div className="mt-3 container mx-auto">
{todoLists.map((todoList) => {
return (
<>
<ul className="flex justify-between mx-20">
<li key={todoList.id}>{todoList.id}</li>
<li>{todoList.value}</li>
<li>
<button onClick={() => deleteListButton(todoList.id)}>
削除
</button>
</li>
</ul>
</>
);
})}
</div>
</div>
todoLists.map ((todolist) = >
上記の部分は、todoListsという複数の要素から、mapを使って引数にあるtodolistというひとつの要素に分けるというイメージです。
ちなみにreturn内の要素を囲っている空のタグはFragment(フラグメント)といってreturn内の構文が複数になる場合に書く必要があるものです。詳しくは公式サイトをご覧ください。
<li key={todoList.id}>{todoList.id}</li>
<li>{todoList.value}</li>
そして、listタグ内に {todoList.id}と{todoList.value}を入力し、todoLists内に格納されたタスクをhandleOnSubmitで定義したidとvalueと合わせて出力するように命令します。
すると、このようにランダムに生成されたID、入力された文字が出力できました!(右の削除ボタンは、このあと解説。)
削除機能(ボタン)の実装
<ul className="flex justify-between mx-20">
<li key={todoList.id}>{todoList.id}</li>
<li>{todoList.value}</li>
<li>
<button onClick={() => deleteListButton(todoList.id)}>
削除
</button>
</li>
</ul>
これで「ボタンが押された際に引数にわたすIDのタスクに対して削除処理を実行する」という命令をすることができます。
続いて、deleteListButtonの処理を実装していきます。
const deleteListButton = (id) => {
const newArray = todoLists.filter((todoLists) => todoLists.id !== id);
setTodoLists(newArray);
};
条件に一致した配列を返してくれるfilterメソッドを活用し「onClickで渡ってきたIDとtodoListsのidが異なるものは残す(=同じものは消す)」という処理ができるようにします。
その処理を new Arrayに代入。変化を起こす変数であるsetTodoListの中で呼び出します。
結果、テスト2のタスクのみを消すことに成功しました!
現時点でのコード
import React, { useState } from "react";
const App = () => {
const [input, setInput] = useState("");
const [todoLists, setTodoLists] = useState([]);
const handleOnSubmit = () => {
const newTodo = {
id: Math.floor(Math.random() * 1000),
value: input,
};
if (input !== "") setTodoLists([...todoLists, newTodo]);
setInput("");
};
const addTodo = (e) => {
e.preventDefault();
handleOnSubmit();
};
const deleteListButton = (id) => {
const newArray = todoLists.filter((todoLists) => todoLists.id !== id);
setTodoLists(newArray);
};
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>
</form>
<div className="mt-3 container mx-auto">
{todoLists.map((todoList) => {
return (
<>
<ul className="flex justify-between mx-20">
<li key={todoList.id}>{todoList.id}</li>
<li>{todoList.value}</li>
<li>
<button onClick={() => deleteListButton(todoList.id)}>
削除
</button> </li>
</ul>
</>
);
})}
</div>
</div>
</>
);
};
まとめ
第1回目の記事では、Todoリストの登録機能と削除機能を実装しました。
特に以下のポイントを抑えることが大事だなと感じています。
- useStateを理解し使えるようになる
- mapで
Udemyの講座で学んできた中には、これ以外にもやり方が複数あって、Reducerを活用した実装方法とかも便利だなと思いました。
第2回目は、 localStrogeを使った永続化と編集機能の実装がテーマになります。現在の状態だと入力した内容がリロードしたら消えてしまうので、リロードしても情報が残るようにするとともに、編集もできるようにしたいです。
学びながらの執筆なので、至らないところがたくさんあると思います。エンジニアの先輩方が読んでくださっていたら、もっとよい書き方のアドバイスいただけたら嬉しいです。