React: イベント処理

最終更新:2026-10-26

イベント処理は、React アプリの神経終末のようなものです。ユーザーがボタンをクリックしたり、テキストを入力したり、フォームを送信したりすると、これらの操作はイベントを通じて処理されます。React のイベントシステムは、ネイティブの DOM イベントよりもスマートで、統一性があります。


1. 学習内容



2. フォームの操作に関する話

(1) 課題:フォーム送信における3つの落とし穴

ボブはユーザー登録フォームを作成しており、ネイティブのJavaScriptでイベントハンドラを記述しています:

JAVASCRIPT
// Native JS:Various compatibility issues and details need to be handled manually

// 1. Get DOM Element
const form = document.getElementById('register-form')
const nameInput = document.getElementById('name')
const submitBtn = document.getElementById('submit-btn')

// 2. Event Binding
form.addEventListener('submit', function(event) {
  event.preventDefault()  // Prevent the page from refreshing
  
  // 3. Manually Retrieving Form Data
  const formData = new FormData(form)
  const data = Object.fromEntries(formData)
  
  // 4. Submit Data
  fetch('/api/register', {
    method: 'POST',
    body: JSON.stringify(data)
  })
})

// 5. Real-Time Input Validation
nameInput.addEventListener('input', function(event) {
  if (event.target.value.length < 2) {
    showError('Name (at least) 2 characters')
  } else {
    hideError()
  }
})

// 6. Prevent duplicate submissions when the Submit button is clicked
let isSubmitting = false
submitBtn.addEventListener('click', function() {
  if (isSubmitting) return
  isSubmitting = true
  submitBtn.disabled = true
  submitBtn.textContent = 'Submitting......'
})

ボブの問題:

(2) Reactにおけるイベント処理

▶ 試してみよう

メリット:コード行数が30行から15行に削減され、DOMの操作が不要となり、イベントの解除に関する問題やブラウザ間の互換性の問題も発生しなくなりました。



3. Reactの複合イベント

ReactのイベントはネイティブなDOMイベントではなく、SyntheticEventsです。Reactはトップレベルでこれらを監視し、クロスブラウザ対応のイベントシステムをシミュレートしています。

特集 ネイティブDOMイベント Reactの複合イベント
バインディング方法 addEventListener / onclick JSXに直接記述
ブラウザの互換性 差異の手動処理が必要 自動標準化
メモリ管理 リスナーは手動で削除する必要がある 自動クリーンアップ
イベントオブジェクト ネイティブイベント SyntheticEvent
気泡の発生を防ぐ event.stopPropagation() 同一(標準化)
ブロックのデフォルト event.preventDefault() 同一(標準化)
100%
graph TB
    A[The user clicks the button] --> B[React Root Node Event Handling]
    B --> C[Create a Synthetic Event Object]
    C --> D[Simulated Bubbles/Capture Phase]
    D --> E[Call JSX Bound to the middle handler]
    
    style B fill:#61dafb,color:#000
    style C fill:#1890ff,color:#fff

出力:

TEXT
...
"Log In" ボタンがクリックされた
ボタンにマウスホバー
マウスがボタンから離れた


4. よくあるイベントのクイックリファレンス

イベント名 トリガー条件 一般的なシナリオ イベントオブジェクトの種類
onClick 要素がクリックされた ボタン、リンク、カード MouseEvent
onChange 入力フィールドの内容の変更 フォームの入力欄、ドロップダウンメニュー ChangeEvent
onSubmit フォームを送信 ログイン/登録/検索 FormEvent
onFocus 要素にフォーカスが当たる 入力フィールドがハイライトされる FocusEvent
onBlur 要素のフォーカスが外れる 入力の検証 FocusEvent
onKeyDown キーを押してください ショートカットキー、Enterキーで検索 KeyboardEvent
onKeyUp プレスリリース リアルタイム検索 KeyboardEvent
onMouseEnter マウスオーバー ホバー効果 MouseEvent
onMouseLeave マウスアウト ホバー効果 MouseEvent
onScroll スクロール 無限スクロール UIEvent

▶ サンプル:よくあるイベントの包括的な概要

▶ 実行例を展開(81 行)
▶ 試してみよう

出力:

TEXT
メールアドレス/パスワードのログインフォーム。メールアドレスが不正な場合 → 赤い枠線 + "The email address format is incorrect."。パスワードが 6 文字未満 → "Password must be at least 6 chars"。送信 → console.log({email, password})


5. イベントパラメータの受け渡し

(1) パラメータの直接渡し

それを矢印関数で囲み、追加のパラメータを直接渡します:

JSX
function UserList({ users, onDelete }) {
  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>
          {user.name}
          {/* Arrow Functions:Incoming user.id As an additional parameter */}
          <button onClick={() => onDelete(user.id)}>
            Delete
          </button>
        </li>
      ))}
    </ul>
  )
}

// Usage
<UserList
  users={users}
  onDelete={(id) => console.log('Delete User:', id)}
/>
▶ 試してみよう

(2) イベントオブジェクトとカスタムパラメータの両方を取得する

JSX
function ColorPicker({ colors, onSelect }) {
  return (
    <div>
      <p>Select a Color:</p>
      {colors.map(color => (
        <button
          key={color}
          onClick={(event) => {
            // event:Native Synthesis Event Object
            // color:Custom Parameters
            onSelect(color)
            console.log('Click here:', event.clientX, event.clientY)
          }}
          style={{
            backgroundColor: color,
            width: '40px',
            height: '40px',
            border: '2px solid #ddd',
            borderRadius: '50%',
            margin: '4px',
            cursor: 'pointer'
          }}
        />
      ))}
    </div>
  )
}
▶ 試してみよう

(3) イベントオブジェクトを渡す3つの方法

メソッド 構文 適用可能なシナリオ
暗黙の引数渡し onClick={handleClick} 追加のパラメータは不要
矢印関数 onClick={() => handleClick(id)} パラメータが必要
同時に通過 onClick={(e) => handleClick(e, id)} イベントオブジェクトとカスタムパラメータが必要


6. デフォルト動作とバブリングの防止

(1) デフォルトの動作を防ぐ:preventDefault()

最も一般的な使用例は、フォームが送信された際にページが再読み込みされないようにすることです:

▶ 試してみよう

(2) 気泡の発生を防ぐ:stopPropagation()

▶ 試してみよう

7. 完全な例:To-Doリスト(包括的なイベント処理)

▶ 実行例を展開(111 行)
▶ 試してみよう

出力:

TEXT
... ('Log In:', { email, password })
"Log In" ボタンがクリックされた
ボタンにマウスホバー
マウスがボタンから離れた

インタラクションの流れ:

  1. テキストボックスにタスク名を入力し、Enter キーを押すか、「追加」ボタンをクリックします → タスクがリストに追加されます
  2. チェックボックスにチェックを入れる → タスクを完了としてマークする(テキストに打ち消し線が入り、灰色で表示される)
  3. ✕ ボタンをクリック → タスクを削除する(ダブルクリックで編集機能が起動しないようにするため、stopPropagation)
  4. 任意のタスクをダブルクリック → 編集用のダイアログボックスが表示されます

▶ サンプル:キーボードショートカットとフォーカスの管理

▶ 実行例を展開(95 行)
▶ 試してみよう

出力:

TEXT
入力フィールドに自動フォーカス。Ctrl+S → "💾 Saved (Ctrl+S)"、Ctrl+Z → "↩️ Undo (Ctrl+Z)"、Escape → クリア、Enter → "✅ Confirm: [text]"。押されたキーが表示される。

出力:

TEXT
サブタイトル:"⌨️ Shortcut Keys Panel"。表示:"⌨️ Shortcut Keys Panel"。入力:Type here、Try the shortcut keys....。useEffect が副作用を管理。useRef で DOM/変数を参照

▶ サンプル:ドラッグ&ドロップによる並べ替えイベントの処理

▶ 試してみよう

出力:

TEXT
ドラッグ可能な 4 つの項目(Apple、Banana、Cherry、Date)。ドラッグして並べ替え。ドラッグ中の項目は青色のハイライト枠で表示される。

出力:

TEXT
表示:"Drag to Reorder"。状態:items(setter: setItems)、dragIdx(setter: setDragIdx)

▶ サンプル:イベントリスナーをラップするためのカスタムフック

▶ 試してみよう

出力:

TEXT
useEventListener フック:DOM イベントリスナーを自動的に追加/削除。アンマウント時にクリーンアップしてメモリリークを防ぐ

出力:

TEXT
状態:pos(setter: setPos)。useEffect が副作用を管理。useCallback でハンドラ関数をメモ化

▶ サンプル:フォーム送信とイベントの組み合わせ

JSX
function SearchForm({ onSearch }) {
  const [query, setQuery] = useState('')
  const [category, setCategory] = useState('all')

  function handleSubmit(e) {
    e.preventDefault()
    onSearch({ query, category })
  }

  function handleReset() {
    setQuery('')
    setCategory('all')
  }

  return (
    <form onSubmit={handleSubmit} style={{ maxWidth: 400, margin: '0 auto' }}>
      <div style={{ display: 'flex', gap: 8, marginBottom: 8 }}>
        <input value={query} onChange={e => setQuery(e.target.value)}
          placeholder="Search..." style={{ flex: 1, padding: 8, borderRadius: 4 }} />
        <select value={category} onChange={e => setCategory(e.target.value)}
          style={{ padding: 8, borderRadius: 4 }}>
          <option value="all">All</option>
          <option value="electronics">Electronics</option>
          <option value="books">Books</option>
        </select>
      </div>
      <div style={{ display: 'flex', gap: 8 }}>
        <button type="submit" style={{ padding: '6px 16px', cursor: 'pointer' }}>Search</button>
        <button type="button" onClick={handleReset} style={{ padding: '6px 16px', cursor: 'pointer' }}>Reset</button>
      </div>
    </form>
  )
}
▶ 試してみよう

出力:

TEXT
検索バー + カテゴリドロップダウン(All/Electronics/Books)。検索送信 {query, category}。リセットで両方のフィールドをクリア。

❓ よくある質問

Q なぜ単に handleClick(id) ではなく、矢印関数 () => handleClick(id) を使うのですか?
A onClick={handleClick(id)} を直接記述すると、React はクリックされるまで待つのではなく、レンダリング中に handleClick(id) を即座に実行してしまいます。これは、(括弧付きの)関数呼び出しであり、(括弧なしの)関数参照ではないためです。正しい方法は、onClick={() => handleClick(id)}(実行を遅延させる矢印関数)または onClick={handleClick}(引数が渡されない場合に関数名を直接参照する)です。
Q 1つの要素に複数の同一のイベントをバインドすることはできますか?
A 2つのonClickを直接バインドすることはできません。解決策:① 同じハンドラー内で複数の関数onClick={() => { fn1(); fn2() }}を呼び出す;② または、その要素を複数のHOCでラップする。通常、要素に必要なイベントハンドラーは1つだけであり、その中に複数のロジックを呼び出すことができます。
Q Reactのシンセティックイベントとネイティブイベントの違いは何ですか?
A ReactのSyntheticEventは、ネイティブイベントをラップしたクロスブラウザ対応のラッパーであり、統一されたAPI(e.preventDefault()やe.stopPropagation()など)を提供し、すべてのブラウザで一貫した動作を実現します。合成イベントはイベントデリゲーションの仕組みに基づいて動作します。つまり、すべてのイベントは個々のDOM要素ではなく、ルートノードにアタッチされます。React 17以降では、イベントはdocumentではなくルートノードにデリゲートされるため、複数のバージョンのReactが共存する場合の競合を防ぐことができます。

📖 まとめ


📝 練習問題

  1. 基本演習(難易度 ⭐):ボタンがクリックされるたびに、表示されているカウントを1ずつ増やす ButtonCounter コンポーネントを作成してください。onClick イベントを使用してください。
  2. 上級演習(難易度 ⭐⭐):「デバウンス検索」を実装した SearchInput コンポーネントを作成してください。この検索機能は、ユーザーが入力を停止してから 500 ms 後に自動的にトリガーされます。onChange および useEffect を使用してください。
  3. 課題(難易度:⭐⭐⭐):ドラッグ&ドロップによる並べ替え機能を実装した DragAndDropList コンポーネントを作成してください。onDragStart、onDragOver、および onDrop イベントを使用してください。リストの項目を新しい位置にドラッグして移動できるようにしてください。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%