ノベルティメディア

media

Todoアプリを作りながら解説するReact Hooks

Todoアプリを作りながら解説するReact Hooks
山田 かおり
Todoアプリを作りながら解説するReact Hooks

こんにちは!アシスタントコーダーの山田です。

ノベルティに入社して約2ヶ月が経ちました!

今回は簡単なTodoアプリを作ってみたので、その解説をしていきます。

(Reactに重きを置いているので、HTMLとCSSの説明は省略します)

今回のコラムでわかること

  • useStateフックを使用したステートの管理方法
  • 配列へのオブジェクトの追加・削除・更新方法

作成したTodoアプリ

今回実装した機能は以下の通りです。

  • タスクの追加
  • タスク完了状態の更新
  • 完了したタスクの削除
新しいタスクをTodoリストに追加し、完了チェックを押して削除ボタンを押すデモ

環境構築

任意のディレクトリで以下のコマンドを実行し、Reactのプロジェクトを新規に作成します。

‘my-app’の部分は、作成するアプリケーションの名前に置き換えてください。

npx create-react-app my-app

プロジェクトが作成されたら、以下のコマンドで開発サーバーを起動します。

npm start

今回作成するディレクトリの構造は以下のようになります。

src/
├── components/
│   └── TodoList/
│       └── index.jsx
└── App.js

App.jsの編集

srcディレクトリ内のApp.jsを編集し、起点となるコンポーネントを定義します。

TodoListコンポーネントは後ほど作成するので、インポートしておきます。

import TodoList from './components/TodoList'

const App = () => {
  return (
    <div>
      <TodoList />
    </div>
  )
}

export default App

また、先ほど作成したApp.jsと同じ階層にcomponents/TodoList/index.jsxを作成し、このindex.jsxファイルにTodoListコンポーネントを作成していきます。

useStateでリスト全体の状態を管理する

useStateはReact Hooksの一つで、ユーザーの入力情報などをコンポーネントに保持させることができます。まずはコンポーネントにuseStateをインポートして、TodoList全体の状態管理をするための関数を準備します。

import { useState } from 'react';

const TodoList = () => {
  const [todos, setTodos] = useState([
    {
      id: 1,
      task: 'タスク1',
      isCompleted: false
    },
    {
      id: 2,
      task: 'タスク2',
      isCompleted: false
    }
  ])
  return (
    <div>
      <h1>Todoリスト</h1>
    </div>
  )
}

export default TodoList

useStateフックを使うと、2つの要素が入った配列が返されます。1つ目の要素は現在の値、2つ目の要素は現在の値を更新する関数です。

  • 現在の値(todos):現在の値で、初回レンダリング時は初期値を持つ。
  • 値を更新する関数(setTodos):ステートを更新すると、コンポーネントが再レンダリングされ、最新の値が反映される。
const [todos, setTodos] = useState([
  {
    id: 1,
    task: 'タスク1',
    isCompleted: false
  },
  {
    id: 2,
    task: 'タスク2',
    isCompleted: false
  }
]) 

今回はステートの引数に

  • id(タスクを識別するためのid)
  • task(タスクの内容)
  • isCompleted(タスクが完了しているかどうかを表す真偽値)

という3つのプロパティを設定しています。各プロパティの値が初期値として設定されます。

Todoリストを表示する

todos配列に格納された先ほどのオブジェクトを、mapメソッドで展開して表示します。mapメソッドは配列の各要素に対して関数を実行し、その結果を新しい配列として返すメソッドです。

以下では、配列の各アイテムを引数として受け取り、JSX要素を返しています。

<li>にはどの要素が変更、追加もしくは削除されたのかを Reactに識別させるためにkey属性を指定します。

<h1>Todoリスト</h1>
<ul>
  {todos.map((todo) => (
    <li key={todo.id}>
      <label>
        <input type='checkbox' />
        <span>{todo.task}</span>
      </label>
    </li>
  ))}
</ul>

このように表示されます。

Todリスト。タイトルと初期設定のタスク1、タスク2が表示されている。

タスク追加機能

新しいタスクを追加し、表示させていきます。

まずは追加したタスクを一時的に保持する新たなステートを用意します。そして、入力フィールドで発生する変更を検知してこのステートを更新するために、onChangeイベントハンドラを設定します。

const [task, setTask] = useState('');//追加したタスクを一時的に保持するステート
<label htmlFor='task'>やること:</label>
<input
  type='text'
  id='task'
  name='task'
  value={task}
  onChange={(e) => {
    setTask(e.target.value)
  }}
/>
<button type='submit'>
  追加
</button>


現在のままでは追加ボタンを押しても何も変わりません。追加ボタンを押したときの処理を実装し、それぞれの要素をformで囲って送信処理を追加します。

onSubmit関数のnewIdでは、todos配列の中からidを抽出し、Math.maxメソッド引数として与えられた数値の中から最大の値を返します。

setTodos()では、スプレッド構文(…prevTodos)が使用されており、これにより現在のTodoリストのすべてのタスクが新しい配列にコピーされ、その配列の最後に新しいオブジェクトが追加されます。

const onSubmit = (e) => {
  e.preventDefault() 
  const newId = todos.length > 0 ? Math.max(...todos.map((todo) => todo.id)) + 1 : 1
  const newTodo = { id: newId, task, isCompleted: false }
  setTodos((prevTodos) => [...prevTodos, newTodo])
  setTask('') 
}
<form onSubmit={onSubmit}>
  <label htmlFor='task'>やること:</label>
  <input
    type='text'
    id='task'
    name='task'
    value={task}
    onChange={(e) => {
      setTask(e.target.value)
    }}
  />
  <button type='submit'>
    追加
  </button>
</form>

テキストフィールドに入力して追加ボタンを押すと、新しいタスクが追加されます。

Todoリスト。テキストフィールドで入力された値の「追加したタスク」が初期設定タスクの下に追加されている。

タスク完了状態の更新

todosステート変数のisCompletedでは、各タスクの完了状態を表しています。初期値は’false’であるため、最初は未完了の状態として設定されています。

タスクの完了状態を更新するために、handleToggleComplete関数を使用します。

この関数では、todos配列を先ほども使用したmapメソッドで展開し、配列の各要素のタスクに対して処理を行います。

todo.id(配列内の各要素の値)とid(関数に渡される値)を比較し、条件が真(つまり、todo.idとidが等しい)場合に、isCompletedの値を反転させています。

const handleToggleComplete = (id) => {
  setTodos((prevTodos) =>
    prevTodos.map((todo) => (todo.id === id ? { ...todo, isCompleted: !todo.isCompleted } : todo))
  )
}

この関数をチェックボックスのonChangeイベントにセットします。

checked属性にステートを入れることで、チェックボックスの選択状態とtodo.isCompletedステートが常に同期されるようになります。

完了したことをわかりやすくするために、条件を出し分けてCSSのクラスを適用し、text-decoration: line-throughで打ち消し線を適用しています。

{todos.map((todo) => (
  <li key={todo.id}>
    <label>
      <input
        type='checkbox'
        checked={todo.isCompleted}
        onChange={() => handleToggleComplete(todo.id)}
      />
      <span className={todo.isCompleted ? styles.completed : ''}>{todo.task}</span>
    </label>
  </li>
))}

完了したタスクを削除する

最後に、完了したタスクを削除するボタンを実装します。

filterメソッドで現在の配列を展開し、isCompletedがfalse(チェックされていない)タスクのみを新しい配列に含めることで、完了したタスクを除外しています。

const handleDeleteCompletedTasks = () => {
  setTodos((prevTodos) => prevTodos.filter((todo) => !todo.isCompleted))
}

この関数を削除を実行するボタンのonClickイベントハンドラとして設定し、クリックすると完了したタスクを削除できるようになります。

<button type='button' onClick={handleDeleteCompletedTasks}>
  完了タスクを削除
</button>

まとめ

今回は簡単なTodoアプリをReactで作成して、useStateや配列メソッドの解説を行いました。

ノベルティでは、Reactを含むモダンなフロントエンド技術を駆使して、クライアントのニーズに合わせた構築技術を提供しています。

興味のある方はぜひお問い合わせを!

この記事をシェアする
山田 かおり

山田 かおり

Assistant Coder

福井県出身。大学で社会福祉を学び、福祉職や事務職を経験しました。 友人との会話をきっかけにコーディング学習を始め、その楽しさから転職を決意。ノベルティではアシスタントコーダーを担当しています。 一人でも多くのユーザーが利用できるWebサイトを制作することが目標です。 趣味は映画鑑賞・ゲーム・料理。喫茶店や名建築を巡ることが好きで、休日はよく散歩します。

Webプロモーション・業務改善は
ノベルティひとつで完結

はじめての依頼にも
全力でサポートさせていただきます

メールでのお問い合わせ

おすすめ記事/ PICKUP

    記事カテゴリー/ CATEGORY

      Webプロモーションや業務改善・DX化

      企業の課題はノベルティひとつで完結

      ホームページ制作などのWeb制作をはじめ、
      システム開発やマーケティング支援などワンストップで対応
      まずはお気軽にお問い合わせください

      お問い合わせ

      お電話またはメールでお気軽にお問い合わせください。