Reactでイベントの伝播を制御!stopPropagationの使い方を初心者向けに解説
生徒
「Reactでボタンをクリックしたときに、親要素にもクリックが伝わってしまうんですが、止める方法はありますか?」
先生
「それならstopPropagationを使います。イベントの伝播を止めるメソッドです。」
生徒
「イベントの伝播って何ですか?」
先生
「簡単に言うと、子要素で発生したイベントが親要素に順番に伝わる仕組みのことです。親子関係があるHTML要素では、クリックやマウス操作が親に伝わることがあります。」
生徒
「なるほど、では実際にReactで止める例を見たいです。」
先生
「ではコードで確認してみましょう!」
1. stopPropagationの基本的な使い方
Reactでは、クリックや入力などのイベントが「子要素 → 親要素」へと順番に伝わる仕組みがあります。この流れをイベントの伝播(バブリング)と呼びます。例えば、親の div と子の button の両方にクリックイベントを設定している場合、子ボタンをクリックすると親のイベントまで実行されてしまいます。意図せず複数の処理が動く原因になるため、必要に応じてevent.stopPropagation()を使って伝播を止めましょう。
下の例では、子ボタンをクリックしたときだけ反応させ、親のクリックイベントは発生しないようにしています。伝播を止める位置を理解すると、意図したとおりにUIを動かしやすくなります。
import React from "react";
function App() {
const handleParentClick = () => {
alert("親要素がクリックされました");
};
const handleChildClick = (event) => {
event.stopPropagation(); // ここで親への伝播を止める
alert("子要素がクリックされました");
};
return (
<div
onClick={handleParentClick}
style={{ padding: "20px", border: "1px solid #000" }}
>
親要素
<button
onClick={handleChildClick}
style={{ marginLeft: "10px" }}
>
子要素ボタン
</button>
</div>
);
}
export default App;
このように、stopPropagationを使えば「どのイベントだけを実行させたいか」を明確に制御できます。フォームやモーダル、メニューなど、画面構成が複雑になるほど役に立つ重要な考え方です。
2. イベント伝播の仕組みを理解しよう
HTMLではイベントは「バブリング」という仕組みで伝わります。バブリングとは、イベントが発生した要素から親要素へ順番に伝わることです。Reactでもこの仕組みは同じで、親子のクリックイベントなどが意図せず連動することがあります。
stopPropagationを使うと、このバブリングを途中で止めることができ、子要素の処理だけを実行したい場合に便利です。フォームの中のボタンやメニュー操作などで役立ちます。
3. 応用例:複数の子要素での制御
複数のボタンやリンクが親要素内にある場合も、stopPropagationを使えば、特定の子要素だけイベントを止めることができます。これにより、複雑なUIでも意図した動作を実現できます。
import React from "react";
function App() {
const handleParentClick = () => alert("親要素がクリックされました");
const handleChildClick = (event, name) => {
event.stopPropagation();
alert(name + "がクリックされました");
};
return (
<div onClick={handleParentClick} style={{ padding: "20px", border: "1px solid #000" }}>
親要素
<button onClick={(e) => handleChildClick(e, "ボタンA")} style={{ marginLeft: "10px" }}>ボタンA</button>
<button onClick={(e) => handleChildClick(e, "ボタンB")} style={{ marginLeft: "10px" }}>ボタンB</button>
</div>
);
}
export default App;
4. 注意点
stopPropagationはとても強力な道具ですが、なんとなく全部のイベントに付けてしまうと、なぜクリックイベントやフォームイベントが動かないのか分かりづらくなり、デバッグに時間がかかってしまいます。「親要素の処理まで止める必要が本当にあるか?」を毎回考えながら使うことが、Reactのイベント伝播を正しく制御するための大事なポイントです。
また、イベント関連では「伝播を止める」と「ブラウザの標準動作を止める」がよく混ざってしまいます。リンクをクリックしたときに画面遷移を止めたい場合はevent.preventDefault()、親へのクリック伝播を止めたい場合はevent.stopPropagation()と、役割が違うので意識して使い分けましょう。どちらもReactのSyntheticEvent内で同じように呼び出せます。
ふたつの違いがイメージしやすいように、簡単なサンプルコードも見ておきましょう。
import React from "react";
function App() {
const handleParentClick = () => {
alert("親のクリックイベントが発火しました");
};
const handleLinkClick = (event) => {
event.preventDefault(); // リンク先への遷移を止める
event.stopPropagation(); // 親への伝播も止める
alert("リンクだけの処理を実行しました");
};
return (
<div
onClick={handleParentClick}
style={{ padding: "20px", border: "1px solid #000" }}
>
親要素
<a
href="https://example.com"
onClick={handleLinkClick}
style={{ marginLeft: "10px" }}
>
このリンクをクリック
</a>
</div>
);
}
export default App;
この例のように、preventDefaultとstopPropagationはセットで使われることも多いですが、「何を止めたいのか」をはっきりさせてから書くと混乱を防げます。モーダルウィンドウ内の閉じるボタン、ドロップダウンメニュー、Reactのフォームコンポーネントなど、イベントが重なりやすい場面ほど設計を丁寧に考えるとよいでしょう。
まとめると、Reactでイベント伝播を扱うときは、まず素直にクリックイベントを書いてみて、「親要素までイベントが届くと困る場所」にだけstopPropagationを追加する意識が大切です。やみくもに付けるのではなく、画面の構造とユーザーの操作を頭の中でシミュレーションしながら、必要最小限の場所に絞って使っていくと、扱いやすくシンプルなコードになります。
5. stopPropagationとイベントキャプチャの違いをおさえよう
Reactのイベント伝播では、よくバブリング(下から上に伝わる)だけが意識されがちですが、仕組みとしてはキャプチャ(上から下へ)という段階もあります。ふだんのonClickなどはバブリング段階で発火しますが、onClickCaptureのような「〜Capture」を使うと、親要素側で先にイベントを受け取ることができます。画面全体のクリックをまとめて監視したいときや、ログ用の処理を集約したいときなどに便利です。
ここでは、親要素でキャプチャ段階とバブリング段階の両方をログに出しつつ、子要素ではstopPropagationで親に届かないよう制御する簡単な例を見てみましょう。Reactなら、こうしたイベントの流れを少ないコードで確認できます。
import React from "react";
function App() {
const handleCapture = () => {
console.log("キャプチャ段階で親が反応しました");
};
const handleBubble = () => {
console.log("バブリング段階で親が反応しました");
};
const handleChildClick = (event) => {
event.stopPropagation();
alert("子要素だけがクリックされました");
};
return (
<div
onClick={handleBubble}
onClickCapture={handleCapture}
style={{ padding: "20px", border: "1px solid #000" }}
>
親要素(キャプチャとバブリングをログ出力)
<button
onClick={handleChildClick}
style={{ marginLeft: "10px" }}
>
子要素ボタン
</button>
</div>
);
}
export default App;
stopPropagationにより親のクリック処理は実行されません)
このように、イベントキャプチャとバブリングの流れを意識しながらstopPropagationを使うと、「どのタイミングでどの処理をさせるか」を細かくコントロールできます。Reactで複雑な画面を組むときほど、イベントの流れを一度図にして整理しておくと、見通しのよいコードになりやすくなります。
6. モーダルウィンドウやオーバーレイでの実践例
stopPropagationが特に役立つ場面として、モーダルウィンドウやダイアログの実装があります。よくあるパターンとして「画面のグレーアウトされた部分(オーバーレイ)をクリックするとモーダルを閉じる」「モーダルの中身をクリックしても閉じない」という動きがあります。このとき、モーダル本体のクリックイベントがオーバーレイに伝わってしまうと、意図せず閉じてしまうので、stopPropagationで親要素への伝播を止めるのが定番です。
下のコードは、オーバーレイのクリックで閉じるモーダルと、その中のコンテンツで伝播を止めるサンプルです。Reactのイベント伝播を理解していれば、こうしたよく見るUIもスムーズに実装できます。
import React, { useState } from "react";
function ModalExample() {
const [open, setOpen] = useState(false);
const handleOverlayClick = () => {
setOpen(false);
};
const handleContentClick = (event) => {
event.stopPropagation(); // オーバーレイへの伝播を止める
};
return (
<div>
<button onClick={() => setOpen(true)}>モーダルを開く</button>
{open && (
<div
onClick={handleOverlayClick}
style={{
position: "fixed",
inset: 0,
backgroundColor: "rgba(0,0,0,0.4)",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<div
onClick={handleContentClick}
style={{
backgroundColor: "#fff",
padding: "20px",
borderRadius: "8px",
minWidth: "260px",
}}
>
<h2>モーダルの中身</h2>
<p>ここをクリックしてもモーダルは閉じません。</p>
<button onClick={() => setOpen(false)}>閉じる</button>
</div>
</div>
)}
</div>
);
}
export default ModalExample;
実際のWebアプリケーションでは、確認ダイアログや設定画面、画像の拡大表示など、モーダルウィンドウを使う場面がたくさんあります。そんなときにstopPropagationを正しく使えると、ユーザーが意図しないタイミングでモーダルが閉じてしまうことを防ぎ、使いやすいインターフェースを実現できます。Reactのイベント制御を練習するときは、こうしたモーダルの例を何度か手を動かして作ってみるのがおすすめです。
7. フォームやメニューで失敗しないための考え方
最後に、フォームやドロップダウンメニューなど、日常的によく使うUIでstopPropagationをどう活かすかを整理しておきましょう。入力フォームを囲む領域全体にクリックイベントを付けている場合、内部のボタンやリンクからのイベントが親に伝わると、予期せぬ送信処理や画面遷移につながることがあります。こうしたときに「どこで伝播を止めるべきか」をあらかじめ設計しておくと、後からバグに悩まされにくくなります。
例えば、カード型の一覧のどこをクリックしても詳細ページへ飛ばしたいが、「編集」ボタンだけは別の処理をしたい、という場面を考えてみましょう。カード全体にはクリック処理を持たせた上で、編集ボタン側でstopPropagationを呼ぶことで、カードへのクリックが発火しないように制御できます。
import React from "react";
function CardList() {
const handleCardClick = (title) => {
alert(title + "の詳細ページへ移動します");
};
const handleEditClick = (event, title) => {
event.stopPropagation();
alert(title + "を編集します");
};
return (
<div>
{["記事A", "記事B", "記事C"].map((title) => (
<div
key={title}
onClick={() => handleCardClick(title)}
style={{
border: "1px solid #ccc",
padding: "12px",
marginBottom: "8px",
cursor: "pointer",
}}
>
<h3>{title}</h3>
<p>カード全体をクリックすると詳細へ移動します。</p>
<button
onClick={(e) => handleEditClick(e, title)}
style={{ marginTop: "4px" }}
>
編集ボタン
</button>
</div>
))}
</div>
);
}
export default CardList;
フォームやメニュー、カード一覧などのUIを設計するときは、「親要素にまとめて付けたイベント」と「子要素にだけ付けたいイベント」を整理し、重なってほしくない部分にだけstopPropagationを使うと理解しやすくなります。Reactでイベントの伝播を丁寧に扱えるようになると、小さなボタン一つの動きまで思い通りにコントロールできるようになり、実務レベルのインタラクティブな画面作りにも自信がついていきます。
まとめ
Reactにおけるイベント伝播の仕組みと、それを制御するためのstopPropagationの使い方は、複雑なユーザーインターフェースを構築するときに非常に重要な基礎となります。特に、親子要素に複数のクリックイベントが設定されている場合、意図しない動作を避けるためにはイベントの流れを理解しながら設計することが欠かせません。今回の記事で学んだのは、子要素で実行したイベントがそのまま親要素へ「バブリング」して伝わるという構造と、それを防ぐための具体的な方法です。Reactは直感的に扱いやすい反面、細かな動作を理解していないと意図せぬイベントが発火してしまうことがあります。だからこそ、イベント伝播の制御は初心者のうちに押さえておきたいポイントです。
また、実際の現場では複数の子要素が並び、それぞれが異なる処理を担うケースが多く存在します。そのような状況でも、クリックイベントが不要に広がらないようにコントロールできるかどうかで、実装の品質や操作性が大きく変わります。とくにモーダル表示やドロップダウンメニュー・ポップアップなど、クリック領域が重なりやすいUIでは、stopPropagationを使う場面が増えます。記事で紹介したような基本構文を理解し、小さなサンプルから触れていくことで、Reactのイベント制御がぐっと身近なものになるでしょう。
ここでは、記事全体の学びを整理しつつ、応用しやすいサンプルコードをもうひとつ紹介します。親要素と複数の子要素を組み合わせ、どのタイミングで伝播を止めるのが最適なのかを体験しながら理解できる構成です。小さな例でも、イベントの動きが視覚的に確かめられることで理解が深まります。
イベント伝播の理解を深めるサンプルコード
import React from "react";
function EventSample() {
const handleWrapperClick = () => {
alert("全体の枠がクリックされました");
};
const handleBoxClick = (event) => {
event.stopPropagation();
alert("内側のボックスがクリックされました");
};
const handleButtonClick = (event) => {
event.stopPropagation();
alert("ボタンがクリックされました");
};
return (
<div
onClick={handleWrapperClick}
style={{ padding: "20px", border: "2px solid #444", width: "260px" }}
>
外側の枠
<div
onClick={handleBoxClick}
style={{ marginTop: "10px", padding: "10px", border: "1px solid #888" }}
>
内側のボックス
<button
onClick={handleButtonClick}
style={{ display: "block", marginTop: "10px" }}
>
内側ボタン
</button>
</div>
</div>
);
}
export default EventSample;
このサンプルでは、外側の枠・内側のボックス・ボタンという三段階の親子関係が設定されています。ボタンを押したとき、伝播が止まらなければ順番にすべてのハンドラーが起動します。しかし、stopPropagationを適切な位置で使うと、意図した要素だけが反応し、他のイベントは静かに無視されます。この構造を実際に試してみると、イベントがどのように流れているかが一目で理解できるようになります。
Reactを使った開発では、動的な画面にユーザーの細かな操作が加わるため、イベント制御はあらゆるところで必要になります。とくに、クリックイベントが意図しない操作につながるとユーザー体験を損なうため、制御の知識があるかどうかで完成度が変わります。今回学んだ内容をもとに、自分のアプリや学習用プロジェクトにイベント制御を組み込んでみると、より深い理解につながるはずです。
最初は難しく見えたイベント伝播の仕組みも、小さなステップで整理していくと自然と理解が進みます。ReactのイベントはSyntheticEventという仕組みによって扱われていますが、基本の考え方は通常のDOMイベントと変わりません。この共通性を知っておくことで、他のフレームワークやネイティブJavaScriptを扱うときにも応用できます。イベントを正しく設計できるとUIの自由度が一気に高まり、複雑な操作性にも対応できるため、ぜひ積極的に実践してみましょう。
生徒
「イベントがどう伝わっていくのか、やっとイメージできました! ボタンから親要素に広がる仕組みなんですね。」
先生
「そうです。そして、その流れを必要に応じて止めることで意図した動きを作れるのがstopPropagationです。」
生徒
「サンプルコードを触ってみたら、どこで伝播が止まるのかがよく分かりました!」
先生
「イベント制御はUI設計の大事な部分です。身につけておくと、複雑な画面を作るときにとても役立ちますよ。」
生徒
「これでReactのイベント処理がかなり自信つきました!もっといろいろ実践してみます!」