[React講座] イベント処理と状態管理(useState)

当ページのリンクには広告が含まれています。
目次

イベント処理と状態管理(useState)

Reactでの「イベント処理」の基本

ブラウザのJSだと:

button.addEventListener('click', () => { ... })

みたいに書きますが、Reactではコンポーネントの中でイベントハンドラを渡す形になります。

export default function App() {
  function handleClick() {
    alert('クリックされました')
  }

  return (
    <button onClick={handleClick}>
      クリック
    </button>
  )
}

ポイント:

  • onClick={handleClick} のように、関数をそのまま渡す(() 付けない)
  • JSXなので onclick ではなく キャメルケース onClick
  • 他にも onChange, onSubmit, onKeyDown など、DOMイベントとほぼ対応

イベントオブジェクト(event)が欲しいときは、普通のJSと同じでOKです。

function handleClick(event) {
  console.log(event.target) // クリックされた要素
}

Reactの「状態」= useState

Reactで「動くUI」を作るときの基本の型は、「状態(state) + イベント ⇒ 画面を書き換える」です。

useState の基本形:

import { useState } from 'react'

export default function App() {
  const [count, setCount] = useState(0)

  // ...
}
  • count:現在の状態
  • setCount:状態を更新するための関数
  • useState(0):初期値 0

イメージ的には:

let count = 0

function setCount(newValue) {
  count = newValue
  再描画()
}

のようなものを、Reactがやってくれている感じです。

シンプルなカウンター

まずは最低限のカウンターから。

import { useState } from 'react'

export default function App() {
  const [count, setCount] = useState(0)

  function handleClick() {
    setCount(count + 1)
  }

  return (
    <div>
      <p>現在のカウント: {count}</p>
      <button onClick={handleClick}>+1</button>
    </div>
  )
}

動き:

  1. 最初のレンダリング時
    • useState(0)count = 0
    • 画面には「現在のカウント: 0」
  2. ボタンをクリック
    • handleClick が呼ばれる
    • setCount(count + 1)setCount(1)
    • Reactが「状態が変わった」と認識して再レンダリング
  3. 再レンダリング時
    • useState の返す値が 1 になる
    • 画面に「現在のカウント: 1」

ポイントは:

  • 状態を変えるのは必ず setCount 経由
  • count を直接書き換えない(count++ はNG)
// NG:こうしても画面は変わらない
function handleClick() {
  count = count + 1  // ただのローカル変数として扱っているだけ
}

Reactは「useState で登録された状態が変わったとき」にだけ再描画する、くらいのイメージを持っておくと整理しやすいです。

状態更新は「非同期的にまとめて」行われる

Reactはパフォーマンスのために、複数の setState をまとめて処理します。

よくある罠:

function App() {
  const [count, setCount] = useState(0)

  function handleDoubleClick() {
    setCount(count + 1)
    setCount(count + 1)
  }

  // ...
}

+1 を2回したから 0 ⇒ 2 だろ」と思いきや、実際は 0 ⇒ 1 にしかなりません。

理由は、2回とも「今の count(=0) を 1 にする」という更新を依頼しているだけだからです。

この場合は、前の状態を元に更新する「関数版」を使うのが正解です。

function handleDoubleClick() {
  setCount((prev) => prev + 1)
  setCount((prev) => prev + 1)
}

ここでは:

  • 1回目:prev は 0 ⇒ prev + 1 = 1
  • 2回目:prev は 1 ⇒ prev + 1 = 2

という風に順番に適用されます。

ルール:

「前の値を使って新しい値を計算するときは、必ずsetX((prev) => 〜) の形にする」

これは実務でもかなり頻繁に使います(カウンター・配列操作・トグルなど全部)。

複数の状態を持つ

コンポーネントは useState を何回でも使えます。

import { useState } from 'react'

export default function App() {
  const [count, setCount] = useState(0)
  const [message, setMessage] = useState('初期メッセージ')
  const [isVisible, setIsVisible] = useState(true)

  return (
    <div>
      <button onClick={() => setCount((prev) => prev + 1)}>
        カウント: {count}
      </button>

      <button onClick={() => setIsVisible((prev) => !prev)}>
        メッセージの表示切り替え
      </button>

      {isVisible && <p>{message}</p>}
    </div>
  )
}

ここでのポイント:

  • setIsVisible((prev) => !prev) で真偽値トグル
  • {isVisible && <p>...</p>} は「条件付きレンダリング」

オブジェクトや配列を状態にする場合

よくやるのが「1個の state にオブジェクト / 配列を入れる」パターンです。

オブジェクト状態

const [user, setUser] = useState({
  name: '山田太郎',
  age: 25,
})

age だけ変えたいとき:

setUser((prev) => ({
  ...prev,
  age: prev.age + 1,
}))
  • ...prev で「前のオブジェクトを展開」
  • 上書きしたいプロパティだけ後ろに書く
  • これを忘れて { age: prev.age + 1 } とやると name が消えるので注意

配列状態

const [items, setItems] = useState(['apple', 'banana'])

要素を追加:

setItems((prev) => [...prev, 'orange'])

要素を削除(例えば index 1 だけ除外):

setItems((prev) => prev.filter((_, index) => index !== 1))

ここでも共通のルール:

  • state は「破壊的変更」しない
  • push, splice, ++ ではなく、新しい配列 / オブジェクトを作って渡す。

入力と状態

import { useState } from 'react'

export default function App() {
  const [name, setName] = useState('')

  function handleChange(e) {
    setName(e.target.value)
  }

  return (
    <div>
      <input
        type="text"
        value={name}
        onChange={handleChange}
        placeholder="名前を入力"
      />
      <p>こんにちは、{name || 'ゲスト'} さん</p>
    </div>
  )
}
  • value={name} ⇒ 入力欄の中身を state で管理
  • onChangesetName(e.target.value) ⇒ 入力されるたびに state 更新
  • 「入力値 = state」の構造を Controlled Component と呼びます

まとめ

  • ReactではイベントハンドラをJSXの属性として渡す(onClick={handleClick}
  • useState で「状態 + セッター関数」を使う
  • 状態を変えるとReactが再レンダリングしてUIを更新する
  • 前の値を参照する更新は必ずsetX(prev => ...) の形にする
  • オブジェクトや配列のstateは破壊的更新禁止、...prevfilter, map で新しい値を作る

<<前へ(ReactコンポーネントとJSXの基本)

>>次へ(コンポーネント設計とデータの流れ)

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次