Reactのイベント処理を完全マスター【初心者向け実例付き】

Webサイトでボタンをクリックしたり、フォームに入力したり。ユーザーの操作に反応してページが変わる、そんな体験は当たり前になっていますよね。

でも、Reactでそれを実装しようとすると、「イベントってどう書くの?」「普通のJavaScriptと何が違うの?」と戸惑う方も多いはずです。

この記事では、Reactにおけるイベント処理について、基礎から実践的なテクニックまで、初心者の方にも分かりやすく解説していきます。

クリック、入力、送信など、あらゆるユーザー操作に対応できるようになりますよ。


スポンサーリンク

Reactのイベントとは

イベントの基本概念

イベントとは、ユーザーがWebページ上で起こすアクション(操作)のことです。

代表的なイベント

  • クリック(ボタンを押す)
  • 入力(テキストを入力する)
  • マウス移動(カーソルを動かす)
  • キーボード操作(キーを押す)
  • フォーム送信(送信ボタンを押す)

これらの操作に反応して、何かを実行するのが「イベント処理」です。

ReactとJavaScriptのイベントの違い

Reactのイベント処理は、標準のJavaScriptと似ていますが、いくつか違いがあります。

命名規則の違い

// 標準のHTML/JavaScript
<button onclick="handleClick()">クリック</button>

// React
<button onClick={handleClick}>クリック</button>

Reactでは「キャメルケース」(先頭は小文字、単語の区切りは大文字)を使います。

イベントハンドラーの指定方法
Reactでは、文字列ではなく関数そのものを渡します。中括弧 {} で囲むのがポイント。

イベントオブジェクトの扱い
Reactは「合成イベント(SyntheticEvent)」という独自のラッパーを使います。ブラウザ間の違いを吸収してくれるので、互換性の心配が減ります。


基本的なイベント処理の書き方

クリックイベントの実装

最もシンプルな例から始めましょう。

基本的な書き方

function ClickButton() {
  function handleClick() {
    alert('ボタンがクリックされました!');
  }

  return (
    <button onClick={handleClick}>
      クリックしてね
    </button>
  );
}

handleClick という関数を定義して、onClick に渡しています。

注意:関数は実行しない

// ❌ 間違い(すぐに実行されてしまう)
<button onClick={handleClick()}>クリック</button>

// ✅ 正しい(関数そのものを渡す)
<button onClick={handleClick}>クリック</button>

括弧 () を付けると、レンダリング時に実行されてしまいます。

インライン関数の使用

簡単な処理なら、その場で関数を定義することもできます。

アロー関数を使う

function InlineButton() {
  return (
    <button onClick={() => alert('クリックされた!')}>
      クリック
    </button>
  );
}

短い処理なら、この書き方もスッキリします。

イベントオブジェクトの取得

イベントハンドラーは、自動的にイベントオブジェクトを受け取ります。

イベントオブジェクトの使用例

function EventButton() {
  function handleClick(event) {
    console.log('イベントタイプ:', event.type);
    console.log('クリックされた要素:', event.target);
  }

  return (
    <button onClick={handleClick}>
      情報を表示
    </button>
  );
}

event パラメータには、クリック位置や対象要素など、様々な情報が含まれています。


よく使うイベントの種類

マウスイベント

マウス操作に関するイベントです。

onClick – クリック

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

要素がクリックされたとき。

onDoubleClick – ダブルクリック

<div onDoubleClick={handleDoubleClick}>
  ダブルクリックしてね
</div>

要素が2回連続でクリックされたとき。

onMouseEnter / onMouseLeave – マウスの出入り

function HoverDiv() {
  function handleMouseEnter() {
    console.log('マウスが入ってきた');
  }

  function handleMouseLeave() {
    console.log('マウスが出ていった');
  }

  return (
    <div 
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
    >
      ここにマウスを持ってきて
    </div>
  );
}

ホバー効果を作るのに便利です。

フォーム関連イベント

入力欄やフォームで使うイベントです。

onChange – 値の変更

function InputField() {
  function handleChange(event) {
    console.log('入力された値:', event.target.value);
  }

  return (
    <input 
      type="text"
      onChange={handleChange}
      placeholder="何か入力してね"
    />
  );
}

テキスト入力、チェックボックス、セレクトボックスなど、値が変わったときに発火します。

onSubmit – フォーム送信

function LoginForm() {
  function handleSubmit(event) {
    event.preventDefault(); // ページ遷移を防ぐ
    console.log('フォームが送信されました');
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" name="username" />
      <button type="submit">送信</button>
    </form>
  );
}

event.preventDefault() は重要。これがないとページがリロードされてしまいます。

onFocus / onBlur – フォーカスの出入り

<input 
  onFocus={() => console.log('フォーカスされた')}
  onBlur={() => console.log('フォーカスが外れた')}
/>

入力欄がアクティブになったとき、離れたとき。

キーボードイベント

キー操作に関するイベントです。

onKeyDown / onKeyUp – キーの押下

function KeyInput() {
  function handleKeyDown(event) {
    console.log('押されたキー:', event.key);

    if (event.key === 'Enter') {
      console.log('Enterキーが押されました');
    }
  }

  return (
    <input 
      type="text"
      onKeyDown={handleKeyDown}
      placeholder="キーを押してね"
    />
  );
}

特定のキー(Enterキーなど)での処理に便利です。


Stateとイベントの連携

useStateと組み合わせる

Reactの真価は、Stateとイベントを組み合わせたときに発揮されます。

カウンターの例

import { useState } from 'react';

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

  function handleIncrement() {
    setCount(count + 1);
  }

  function handleDecrement() {
    setCount(count - 1);
  }

  return (
    <div>
      <p>カウント: {count}</p>
      <button onClick={handleIncrement}>+1</button>
      <button onClick={handleDecrement}>-1</button>
    </div>
  );
}

ボタンをクリックするたびに、Stateが更新されて画面が再レンダリングされます。

フォーム入力の管理

ユーザーの入力をStateで管理する「制御されたコンポーネント」のパターンです。

テキスト入力の管理

import { useState } from 'react';

function TextInput() {
  const [text, setText] = useState('');

  function handleChange(event) {
    setText(event.target.value);
  }

  return (
    <div>
      <input 
        type="text"
        value={text}
        onChange={handleChange}
        placeholder="名前を入力"
      />
      <p>入力された名前: {text}</p>
    </div>
  );
}

入力値をリアルタイムで表示できています。

複数の入力項目の管理

オブジェクトを使って、複数の入力をまとめて管理できます。

フォーム全体の管理

import { useState } from 'react';

function RegistrationForm() {
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    age: ''
  });

  function handleChange(event) {
    const { name, value } = event.target;
    setFormData({
      ...formData,
      [name]: value
    });
  }

  function handleSubmit(event) {
    event.preventDefault();
    console.log('送信データ:', formData);
  }

  return (
    <form onSubmit={handleSubmit}>
      <input 
        name="username"
        value={formData.username}
        onChange={handleChange}
        placeholder="ユーザー名"
      />
      <input 
        name="email"
        value={formData.email}
        onChange={handleChange}
        placeholder="メールアドレス"
      />
      <input 
        name="age"
        type="number"
        value={formData.age}
        onChange={handleChange}
        placeholder="年齢"
      />
      <button type="submit">登録</button>
    </form>
  );
}

1つの handleChange 関数で、すべての入力を処理しています。


イベント処理の応用テクニック

イベントハンドラーに引数を渡す

ボタンごとに異なる値を渡したい場合があります。

アロー関数でラップする

function ButtonList() {
  function handleClick(id) {
    console.log('クリックされたID:', id);
  }

  return (
    <div>
      <button onClick={() => handleClick(1)}>ボタン1</button>
      <button onClick={() => handleClick(2)}>ボタン2</button>
      <button onClick={() => handleClick(3)}>ボタン3</button>
    </div>
  );
}

アロー関数で囲むことで、引数付きの関数を渡せます。

bindメソッドを使う方法

<button onClick={handleClick.bind(null, 1)}>ボタン1</button>

こちらの方法もありますが、アロー関数の方が直感的です。

イベントの伝播を制御

親子関係にある要素では、イベントが伝播します。

イベントバブリングの例

function BubbleExample() {
  function handleParentClick() {
    console.log('親要素がクリックされた');
  }

  function handleChildClick(event) {
    event.stopPropagation(); // 伝播を止める
    console.log('子要素がクリックされた');
  }

  return (
    <div onClick={handleParentClick}>
      <button onClick={handleChildClick}>
        子要素のボタン
      </button>
    </div>
  );
}

event.stopPropagation() で、親要素へのイベント伝播を防げます。

デフォルト動作の防止

特定の要素は、デフォルトで特定の動作をします。

リンクのデフォルト動作を防ぐ

function CustomLink() {
  function handleClick(event) {
    event.preventDefault(); // ページ遷移を防ぐ
    console.log('カスタム処理を実行');
  }

  return (
    <a href="https://example.com" onClick={handleClick}>
      クリックしても遷移しません
    </a>
  );
}

フォーム送信やリンククリックの動作を、自分で制御できます。

条件付きイベント処理

特定の条件でのみ処理を実行したい場合。

条件分岐の例

function ConditionalButton() {
  const [isEnabled, setIsEnabled] = useState(true);

  function handleClick() {
    if (!isEnabled) {
      console.log('ボタンが無効です');
      return;
    }
    console.log('処理を実行');
  }

  return (
    <div>
      <button onClick={handleClick}>
        実行
      </button>
      <button onClick={() => setIsEnabled(!isEnabled)}>
        有効/無効を切り替え
      </button>
    </div>
  );
}

パフォーマンスの最適化

useCallbackでメモ化

イベントハンドラーを毎回再作成しないようにする最適化です。

useCallbackの使用

import { useState, useCallback } from 'react';

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

  const handleIncrement = useCallback(() => {
    setCount(prevCount => prevCount + 1);
  }, []); // 依存配列が空なので、関数は1回だけ作成される

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

子コンポーネントに関数を渡す場合、この最適化が効果的です。

インライン関数の注意点

毎回新しい関数が作られるため、パフォーマンスに影響することがあります。

避けるべきパターン

// 大量のリスト項目で、各項目がこれを使うと遅くなる
{items.map(item => (
  <Item 
    key={item.id}
    onClick={() => handleClick(item.id)}
  />
))}

改善方法

function ItemContainer() {
  function handleItemClick(id) {
    console.log('クリックされたID:', id);
  }

  return (
    <>
      {items.map(item => (
        <Item 
          key={item.id}
          id={item.id}
          onItemClick={handleItemClick}
        />
      ))}
    </>
  );
}

// 子コンポーネント側
function Item({ id, onItemClick }) {
  return (
    <div onClick={() => onItemClick(id)}>
      項目 {id}
    </div>
  );
}

よくある間違いとその対策

間違い1:イベントハンドラーをすぐ実行してしまう

// ❌ 間違い
<button onClick={handleClick()}>クリック</button>

// ✅ 正しい
<button onClick={handleClick}>クリック</button>

括弧を付けると、レンダリング時に実行されます。

間違い2:thisの参照エラー

クラスコンポーネントでよくある問題ですが、関数コンポーネントでは起きません。

クラスコンポーネントの場合

class MyComponent extends React.Component {
  handleClick() {
    console.log(this); // undefinedになる
  }

  render() {
    return <button onClick={this.handleClick}>クリック</button>;
  }
}

解決策:アロー関数を使う

class MyComponent extends React.Component {
  handleClick = () => {
    console.log(this); // 正しく参照できる
  }

  render() {
    return <button onClick={this.handleClick}>クリック</button>;
  }
}

関数コンポーネントなら、この問題は発生しません。

間違い3:preventDefault の忘れ

フォーム送信で、ページがリロードされてしまう問題。

// ❌ 間違い(ページがリロードされる)
function Form() {
  function handleSubmit() {
    console.log('送信');
  }

  return <form onSubmit={handleSubmit}>...</form>;
}

// ✅ 正しい
function Form() {
  function handleSubmit(event) {
    event.preventDefault(); // これが必要
    console.log('送信');
  }

  return <form onSubmit={handleSubmit}>...</form>;
}

実践的な例:Todoリスト

学んだ知識を活かして、シンプルなTodoリストを作ってみましょう。

完全なコード例

import { useState } from 'react';

function TodoList() {
  const [todos, setTodos] = useState([]);
  const [inputValue, setInputValue] = useState('');

  function handleInputChange(event) {
    setInputValue(event.target.value);
  }

  function handleSubmit(event) {
    event.preventDefault();

    if (inputValue.trim() === '') return;

    const newTodo = {
      id: Date.now(),
      text: inputValue,
      completed: false
    };

    setTodos([...todos, newTodo]);
    setInputValue('');
  }

  function handleToggle(id) {
    setTodos(todos.map(todo => 
      todo.id === id 
        ? { ...todo, completed: !todo.completed }
        : todo
    ));
  }

  function handleDelete(id) {
    setTodos(todos.filter(todo => todo.id !== id));
  }

  return (
    <div>
      <h2>Todoリスト</h2>

      <form onSubmit={handleSubmit}>
        <input 
          type="text"
          value={inputValue}
          onChange={handleInputChange}
          placeholder="新しいタスクを入力"
        />
        <button type="submit">追加</button>
      </form>

      <ul>
        {todos.map(todo => (
          <li key={todo.id}>
            <input 
              type="checkbox"
              checked={todo.completed}
              onChange={() => handleToggle(todo.id)}
            />
            <span style={{ 
              textDecoration: todo.completed ? 'line-through' : 'none' 
            }}>
              {todo.text}
            </span>
            <button onClick={() => handleDelete(todo.id)}>
              削除
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

このコードには、以下のイベント処理が含まれています。

  • フォーム送信(onSubmit)
  • テキスト入力(onChange)
  • チェックボックスの切り替え(onChange)
  • 削除ボタンのクリック(onClick)

まとめ:Reactイベント処理をマスターしよう

Reactのイベント処理について、基礎から実践まで解説してきました。

この記事のポイント

  • Reactのイベントはキャメルケースで書く
  • イベントハンドラーは関数そのものを渡す(実行しない)
  • onClick、onChange、onSubmit など様々なイベントがある
  • Stateとイベントを組み合わせて、インタラクティブなUIを作る
  • event.preventDefault() でデフォルト動作を制御
  • useCallback でパフォーマンス最適化

イベント処理は、ユーザーとアプリケーションをつなぐ架け橋です。

この知識があれば、クリック、入力、送信など、あらゆるユーザー操作に対応できます。

次のステップ

  • 実際に手を動かして、小さなアプリを作ってみる
  • イベントとStateの組み合わせに慣れる
  • useEffectと組み合わせて、より複雑な処理に挑戦

Reactのイベント処理をマスターして、魅力的なインタラクティブアプリを作っていきましょう!

コメント

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