Reactで学ぶ 予定管理アプリ開発(第3回)~ useStateによる予定の追加・削除~

GitHubでつくって学ぶ

Amazon.co.jp

はじめに

前回は、予定データをコンポーネントに分割し、propsで受け渡して一覧表示するところまで作った。ただし、表示していたのはコード内に直接書いた固定のデータだった。

今回は、いよいよReactの中心的な機能であるuseStateを使い、画面上から予定を追加・削除できるようにする。ここまでできると、ようやく「アプリらしい」動きになってくる。

今回やること

  • useStateを使って、予定の配列を状態(state)として管理する
  • 入力フォームから新しい予定を追加する
  • 一覧から特定の予定を削除する

今回もツールが生成したpackage.jsonなどのファイルは変更していない。読むべきは、この後出てくるApp.jsxcomponents/配下の2ファイルだけである(詳しくは第1回を参照)。

useStateとは

useStateは、コンポーネントの中に「変化する値」を持たせるためのReactの機能(Hook)である。

src/App.jsx(抜粋)

const [schedules, setSchedules] = useState(initialSchedules)
  • schedules:現在の予定一覧(値そのもの)
  • setSchedules:予定一覧を更新するための関数
  • initialSchedules:最初に表示しておく初期値

Ver.2までは、schedulesをただの定数として扱っていたが、useStateで管理することで、「値が変わったら画面も自動的に再描画される」という仕組みが手に入る。

予定を追加する

追加処理は、既存の配列に新しい要素を足した「新しい配列」を作り、それで置き換える形で行う。

src/App.jsx(抜粋)

const handleAdd = ({ date, title }) => {
  const newSchedule = { id: nextId, date, title }
  nextId += 1
  setSchedules((prev) => [...prev, newSchedule])
}

[...prev, newSchedule]という書き方(スプレッド構文)がポイントである。prev(今までの配列)の中身をすべて展開したうえで、末尾にnewScheduleを加えた新しい配列を作っている。

Reactでは、配列や状態を直接書き換えるのではなく、「新しいものを作って置き換える」という考え方が基本になる。この点は最初つまずきやすいので、意識しておくとよい。

予定を削除する

削除は.filter()を使い、「消したいid以外」を残した新しい配列を作る。

src/App.jsx(抜粋)

const handleDelete = (id) => {
  setSchedules((prev) => prev.filter((schedule) => schedule.id !== id))
}

入力フォームを作る

フォーム自体にも、入力中の値を保持するためのuseStateを使う。

src/components/ScheduleForm.jsx

function ScheduleForm({ onAdd }) {
  const [date, setDate] = useState('')
  const [title, setTitle] = useState('')

  const handleSubmit = (event) => {
    event.preventDefault()
    if (!date || !title) return

    onAdd({ date, title })
    setDate('')
    setTitle('')
  }

  return (
    <form className="schedule-form" onSubmit={handleSubmit}>
      <input type="date" value={date} onChange={(event) => setDate(event.target.value)} />
      <input
        type="text"
        value={title}
        onChange={(event) => setTitle(event.target.value)}
        placeholder="予定のタイトル"
      />
      <button type="submit">追加</button>
    </form>
  )
}

フォームの送信時(handleSubmit)に親から渡されたonAddを呼び出し、実際の追加処理はApp側に任せている。送信後はsetDate('') setTitle('')で入力欄を空に戻している。

削除ボタンをコンポーネントに追加する

Ver.2のScheduleItemに、削除ボタンとonDeletepropsを追加した。

src/components/ScheduleItem.jsx

function ScheduleItem({ title, date, onDelete }) {
  return (
    <li className="schedule-item">
      <span className="schedule-item__date">{date}</span>
      <span className="schedule-item__title">{title}</span>
      <button type="button" onClick={onDelete}>削除</button>
    </li>
  )
}

呼び出す側(App.jsx)では、どのidを削除するかをその場で指定した関数を渡している。

src/App.jsx(抜粋)

<ScheduleItem
  key={schedule.id}
  title={schedule.title}
  date={schedule.date}
  onDelete={() => handleDelete(schedule.id)}
/>

実行結果

npm run devで開発サーバーを起動し、ブラウザで確認する。終了する場合はターミナル上でCtrl + Cを押す(Ctrl + Zは一時停止するだけなので使わない)。

日付とタイトルを入力して「追加」を押すと一覧に加わり、各行の「削除」ボタンを押すとその予定が消える。

今回のまとめ

  • useStateで配列を状態として管理すると、値の変化に応じて画面が自動的に再描画される
  • 追加はスプレッド構文、削除は.filter()で「新しい配列を作って置き換える」のがReact流のやり方
  • フォームの入力値もuseStateで管理する

ただし、現状ではブラウザを再読み込みすると、追加した予定は消えてしまう。次回はuseEffectlocalStorageを使い、データをブラウザに保存して再読み込みしても消えないようにする。

ソースコード

今回のソースコードは、私のGitHubで公開している。以下のリンクを参照していたただきたい。

GitHub:https://github.com/HappyTalk10/pc-labo-react-learning/tree/main/03_usestate-todo


コメント

タイトルとURLをコピーしました