なぜReactのイベントは「賢い」のか?――イベント委譲(Event Delegation)の裏側を解剖する
現場でReactを書いていると、つい「JSXの中に書いた`onClick`がどう動いているか」なんて深く考えずにコードを書き進めてしまうものです。しかし、中級者から一歩上のエンジニアを目指すなら、Reactが裏側で何をしているのかを知っておく必要がある。
特に「イベント委譲(Event Delegation)」は、Reactのパフォーマンスの根幹を支える非常にエレガントな仕組みです。今回は、この仕組みを深掘りし、実務でどう活かすべきかを解説します。
—
1. ブラウザの「素のイベント」とReactの「合成イベント」
まず、前提を揃えよう。DOM要素に直接 `addEventListener` をアタッチする昔ながらの手法では、要素が増えるたびにメモリを消費する。例えば、1000個のリスト項目一つひとつにクリックイベントを紐付けると、ブラウザは1000個のリスナーを管理しなければならず、メモリ効率が最悪になる。
ここで登場するのが「イベント委譲」だ。親要素にイベントリスナーを一つだけ配置し、バブリングしてきたイベントをキャッチして「誰がクリックされたか」を判断する。
Reactは、この仕組みを最初から標準で採用している。
Reactの魔法:ルートへの集約
React(17以降)は、個々のコンポーネントにイベントを直接つけるのではなく、Reactツリーのルート(通常は `div#root`)にイベントリスナーを一つだけ設置する。これを「合成イベント(SyntheticEvent)」と呼ぶ。
この仕様のおかげで、コンポーネントが何千個あっても、ブラウザが管理する生のアタッチメントは一つだけ。Reactが内部的にイベントの発生源(`event.target`)を特定し、該当するコンポーネントのハンドラを呼び出してくれるんだ。
—
2. なぜこれが実務で重要なのか
「仕組みはわかったけど、結局何が嬉しいの?」と思うかもしれない。現場で意識すべきは、以下の2点だ。
1. メモリ管理の最適化: コンポーネントのアンマウント時に、逐一 `removeEventListener` を手動で書く必要がない。Reactの仮想DOMが同期してくれるため、メモリリークのリスクが圧倒的に低い。
2. クロスブラウザの吸収: ブラウザごとに微妙に異なるイベント挙動を、Reactがラップして統一してくれる。`e.stopPropagation()` を書けば、ブラウザの違いを気にせず「止まるべきところで止まる」。
—
3. 実践:イベント委譲を活かした設計パターン
実務でよくある「リスト内の特定アイテムをクリックしたときに処理を走らせる」コードを例にしよう。これを「コンポーネントごとにハンドラを作る」のではなく、「親で一括管理する」イメージで書くと、非常にクリーンになる。
import React, { useCallback } from ‘react’;
// リストアイテムの型定義
type Item = { id: number; label: string };
const ItemList = ({ items }: { items: Item[] }) => {
// 親要素にハンドラを一つだけ定義する(イベント委譲のメリットを享受)
// Reactが内部的にこの関数を呼び出す際、target情報を適切にフィルタリングしてくれる
const handleItemClick = useCallback((e: React.MouseEvent
// data-id属性を使って、誰がクリックされたかを判定する
const target = e.target as HTMLElement;
const id = target.getAttribute(‘data-id’);
if (id) {
console.log(`アイテム ${id} がクリックされました`);
// ここでビジネスロジックを実行
}
}, []);
return (
// 親要素でイベントをキャッチすることで、子要素の再レンダリングコストを抑える
-
{items.map((item) => (
{item.label}
))}
);
};
このコードのポイント
- `data-id` の活用: DOMにカスタム属性を持たせることで、`target` から動的に情報を引き出せる。これは、リストが動的に増減するような大規模アプリケーションで非常に強い。
- 関数の安定化: `useCallback` でハンドラをメモ化することで、リストが再レンダリングされてもイベントハンドラの再生成を防ぐ。小さな最適化の積み重ねが、アプリのサクサク感に直結する。
—
4. チーフアーキテクトからのアドバイス
最後に一つだけ、現場でよくあるミスを指摘しておこう。
「stopPropagationの多用は禁物だ」。
Reactのイベント委譲は、ルートに向かってイベントが伝播(バブリング)することで成り立っている。特定のコンポーネントで安易に `e.stopPropagation()` を使いすぎると、上位コンポーネントで仕掛けていたグローバルなクリック検知(例:モーダルの外側をクリックして閉じる処理など)が機能しなくなる。
イベント委譲の仕組みを理解しているなら、「バブリングはReactの味方である」ということを思い出してほしい。無理に遮断せず、必要に応じて親へ伝播させる設計こそが、ReactらしいスケーラブルなUIを作るコツだ。
技術はただ動けばいいわけじゃない。その裏で何が起きているかを感じ取りながらコードを書く。その感覚が、君を一段上のエンジニアへと引き上げてくれるはずだ。また現場で会おう。

コメント