Reactのイベント処理

2025-08-08

Reactにおけるイベント処理は、ユーザーインタラクションに対応するための重要な概念です。この章では、特にonClickを中心としたイベント処理の基本から実践的な使い方まで、初心者向けに詳細に解説します。

Reactのイベント処理の基本

従来のHTMLとの違い

Reactのイベント処理は通常のHTMLと似ていますが、いくつかの重要な違いがあります:

  1. イベント名はキャメルケースonclickonClick
  2. 関数を直接渡す:文字列ではなく関数そのものを渡す
  3. デフォルト動作の防止return falseでは無効にできず、明示的にpreventDefaultを呼ぶ
// HTML
<button onclick="handleClick()">クリック</button>

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

基本的なクリックイベントの例

function ClickExample() {
  const handleClick = () => {
    console.log('ボタンがクリックされました!');
  };

  return <button onClick={handleClick}>クリックしてください</button>;
}

イベントハンドラーの書き方

関数の定義方法

イベントハンドラーには主に3つの定義方法があります。

1. コンポーネント内で関数を定義

function ButtonComponent() {
  function handleClick() {
    console.log('クリックされました');
  }

  return <button onClick={handleClick}>ボタン</button>;
}

2. アロー関数を使用

function ButtonComponent() {
  const handleClick = () => {
    console.log('クリックされました');
  };

  return <button onClick={handleClick}>ボタン</button>;
}

3. インラインで直接定義(シンプルな処理向け)

function ButtonComponent() {
  return (
    <button onClick={() => console.log('クリックされました')}>
      ボタン
    </button>
  );
}

イベントオブジェクトの利用

Reactはイベントハンドラーに合成イベント(SyntheticEvent)オブジェクトを渡します。

function EventObjectExample() {
  const handleClick = (event) => {
    console.log('イベントタイプ:', event.type); // "click"
    console.log('クリック位置:', event.clientX, event.clientY);
    console.log('ターゲット要素:', event.target);
  };

  return <button onClick={handleClick}>位置をログに出力</button>;
}

様々なイベントタイプ

一般的なイベント一覧

イベント名説明使用例
onClickクリック時ボタン、リンク
onChange値が変更された時入力欄、セレクトボックス
onSubmitフォーム送信時フォーム
onFocus要素がフォーカスを得た時入力欄
onBlur要素がフォーカスを失った時入力欄
onMouseEnterマウスが要素に入った時ホバー効果
onMouseLeaveマウスが要素から出た時ホバー効果
onKeyDownキーが押された時キーボード操作
onKeyUpキーが離された時キーボード操作

マウスイベントの例

function MouseEventExample() {
  const handleMouseEnter = () => {
    console.log('マウスが要素に入りました');
  };

  const handleMouseLeave = () => {
    console.log('マウスが要素から出ました');
  };

  return (
    <div
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
      style={{ padding: '20px', backgroundColor: 'lightblue' }}
    >
      マウスをここに動かしてみてください
    </div>
  );
}

キーボードイベントの例

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

  return (
    <input
      type="text"
      onKeyDown={handleKeyDown}
      placeholder="Enterキーを押してみてください"
    />
  );
}

イベントハンドラーとStateの連携

クリックカウンターの実装

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

  const increment = () => {
    setCount(count + 1);
  };

  const decrement = () => {
    setCount(count - 1);
  };

  return (
    <div>
      <p>現在のカウント: {count}</p>
      <button onClick={increment}>増やす</button>
      <button onClick={decrement}>減らす</button>
    </div>
  );
}

複数のボタンで異なる処理を実行

function MultipleButtons() {
  const [message, setMessage] = useState('ボタンをクリックしてください');

  const showHello = () => {
    setMessage('こんにちは!');
  };

  const showGoodbye = () => {
    setMessage('さようなら!');
  };

  const resetMessage = () => {
    setMessage('ボタンをクリックしてください');
  };

  return (
    <div>
      <p>{message}</p>
      <button onClick={showHello}>挨拶</button>
      <button onClick={showGoodbye}>別れ</button>
      <button onClick={resetMessage}>リセット</button>
    </div>
  );
}

イベント処理のベストプラクティス

ハンドラー関数の命名規則

  • イベントハンドラーは「handle」で始めるのが一般的
  • 例:handleClick, handleSubmit, handleChange

インライン関数の注意点

インライン関数は簡潔に書けますが、パフォーマンスに影響する可能性があります。

// 推奨(コンポーネントの再レンダリング時に再作成されない)
function Component() {
  const handleClick = () => { /* ... */ };
  return <button onClick={handleClick}>ボタン</button>;
}

// 非推奨(再レンダリングごとに新しい関数が作成される)
function Component() {
  return <button onClick={() => { /* ... */ }}>ボタン</button>;
}

引数を渡す方法

イベントハンドラーに追加の引数を渡す必要がある場合:

function ItemList() {
  const items = ['アイテム1', 'アイテム2', 'アイテム3'];

  const handleItemClick = (itemName, event) => {
    console.log(`${itemName}がクリックされました`, event);
  };

  return (
    <ul>
      {items.map((item) => (
        <li key={item}>
          <button onClick={(e) => handleItemClick(item, e)}>
            {item}
          </button>
        </li>
      ))}
    </ul>
  );
}

イベント伝搬(バブリング)とキャプチャリング

イベント伝搬の基本

ReactでもDOMと同じイベント伝搬モデル(バブリングとキャプチャリング)が適用されます。

function EventBubblingExample() {
  const handleParentClick = () => {
    console.log('親要素がクリックされました');
  };

  const handleChildClick = (e) => {
    console.log('子要素がクリックされました');
    e.stopPropagation(); // 伝搬を停止
  };

  return (
    <div onClick={handleParentClick} style={{ padding: '20px', backgroundColor: 'lightgray' }}>
      <button onClick={handleChildClick}>子要素のボタン</button>
    </div>
  );
}

キャプチャフェーズの利用

onClickCaptureのように、イベント名にCaptureを追加するとキャプチャフェーズで処理されます。

function EventCaptureExample() {
  const handleParentCapture = () => {
    console.log('キャプチャフェーズ: 親要素');
  };

  const handleChildClick = () => {
    console.log('バブリングフェーズ: 子要素');
  };

  return (
    <div onClickCapture={handleParentCapture} style={{ padding: '20px', backgroundColor: 'lightblue' }}>
      <button onClick={handleChildClick}>クリック</button>
    </div>
  );
}

カスタムコンポーネントでのイベント処理

コンポーネントにイベントハンドラーを渡す

function CustomButton({ onClick, children }) {
  return (
    <button 
      onClick={onClick}
      style={{ 
        padding: '10px 15px',
        backgroundColor: '#4CAF50',
        color: 'white',
        border: 'none',
        borderRadius: '4px'
      }}
    >
      {children}
    </button>
  );
}

function App() {
  const handleButtonClick = () => {
    alert('カスタムボタンがクリックされました!');
  };

  return (
    <CustomButton onClick={handleButtonClick}>
      クリックしてください
    </CustomButton>
  );
}

イベントハンドラーを拡張する

function EnhancedButton({ onClick, children }) {
  const handleClick = (event) => {
    console.log('ボタンがクリックされました:', event);
    if (onClick) {
      onClick(event); // 親から渡されたハンドラーを呼び出す
    }
  };

  return <button onClick={handleClick}>{children}</button>;
}

function App() {
  const handleParentClick = () => {
    console.log('親コンポーネントのハンドラー');
  };

  return (
    <EnhancedButton onClick={handleParentClick}>
      拡張ボタン
    </EnhancedButton>
  );
}

よくある間違いとデバッグ

よくあるエラーと解決策

  1. 関数ではなく関数呼び出しを渡してしまう
// 誤り(レンダリング時に即時実行される)
<button onClick={handleClick()}>クリック</button>

// 正しい
<button onClick={handleClick}>クリック</button>
  1. イベントオブジェクトを正しく扱えない
// 誤り(eventがundefined)
<button onClick={() => handleClick(event)}>クリック</button>

// 正しい
<button onClick={handleClick}>クリック</button>
// または
<button onClick={(e) => handleClick(e)}>クリック</button>
  1. クラスコンポーネントでのthisのバインド忘れ
class MyComponent extends React.Component {
  handleClick() {
    console.log('クリック'); // thisがundefined
  }

  // 解決法1: コンストラクタでバインド
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }

  // 解決法2: アロー関数を使用
  handleClick = () => {
    console.log('クリック');
  }

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

デバッグ技法

  1. コンソールログの活用
   const handleClick = (event) => {
     console.log('イベント詳細:', event);
     console.log('ターゲット:', event.target);
     console.log('現在の状態:', this.state); // クラスコンポーネントの場合
   };
  1. React Developer Tools
  • イベントハンドラーが正しく渡されているか確認
  • コンポーネントの再レンダリングを調査
  1. デバッガーステートメント
   const handleClick = (event) => {
     debugger; // 実行を一時停止
     // ...
   };

実践的なイベント処理の例

ツールチップの実装

function Tooltip({ text, children }) {
  const [isVisible, setIsVisible] = useState(false);

  return (
    <div style={{ position: 'relative', display: 'inline-block' }}>
      <span
        onMouseEnter={() => setIsVisible(true)}
        onMouseLeave={() => setIsVisible(false)}
      >
        {children}
      </span>
      {isVisible && (
        <div
          style={{
            position: 'absolute',
            bottom: '100%',
            left: '50%',
            transform: 'translateX(-50%)',
            backgroundColor: 'black',
            color: 'white',
            padding: '5px',
            borderRadius: '4px',
            marginBottom: '5px'
          }}
        >
          {text}
        </div>
      )}
    </div>
  );
}

// 使用例
<Tooltip text="これはツールチップです">
  <button>ホバーしてください</button>
</Tooltip>

ダブルクリック防止ボタン

function DebounceButton({ onClick, children }) {
  const [isDisabled, setIsDisabled] = useState(false);

  const handleClick = (event) => {
    if (isDisabled) return;

    setIsDisabled(true);
    onClick(event);

    setTimeout(() => {
      setIsDisabled(false);
    }, 1000); // 1秒間無効化
  };

  return (
    <button onClick={handleClick} disabled={isDisabled}>
      {isDisabled ? '処理中...' : children}
    </button>
  );
}

// 使用例
<DebounceButton onClick={() => console.log('処理開始')}>
  クリック
</DebounceButton>

まとめ:Reactイベント処理の重要なポイント

  1. イベント名はキャメルケースonClick, onMouseEnterなど
  2. ハンドラーに関数を渡すonClick={handleClick}handleClick()ではない)
  3. イベントオブジェクト:Reactの合成イベントが渡される
  4. Stateとの連携:イベントハンドラー内でStateを更新可能
  5. パフォーマンス考慮:インライン関数の多用を避ける
  6. イベント伝搬stopPropagation()で伝搬を制御

イベント処理はReactアプリケーションにインタラクティビティを追加するための基本技術です。適切に実装することで、ユーザーフレンドリーなUIを作成できます。次に学ぶライフサイクルメソッドやフォーム処理と組み合わせることで、さらに高度なインタラクションを実現できるようになります。