「ボタンをクリックした時の情報を取得したい…」
「event.targetとevent.currentTargetの違いが分からない」
Reactでのイベント処理は、初心者がつまずきやすいポイントの1つです。
この記事では、Reactのイベントオブジェクト(SyntheticEvent)について、基礎から実践的な使い方まで分かりやすく解説していきますね。
Reactのイベントオブジェクト基本

SyntheticEventって何?
SyntheticEvent(シンセティックイベント)は、Reactが独自に提供するイベントオブジェクトです。
ブラウザのネイティブイベントをラップ(包み込む)して、統一されたインターフェースを提供します。
これにより、ブラウザごとの挙動の違いを気にせず、同じコードで動作するんですね。
なぜSyntheticEventが必要なの?
ブラウザによって、イベント処理の仕様が微妙に異なります。
クロスブラウザ対応
Chrome、Firefox、Safari、Edgeなど、すべてのブラウザで同じように動作します。
パフォーマンス最適化
イベントプーリングという仕組みで、メモリ効率が向上していました(React 17以前)。
一貫性
統一されたAPIで、学習コストが低くなりますよ。
ネイティブイベントとの違い
基本的なプロパティやメソッドは、ネイティブイベントと同じです。
共通点:
- preventDefault()
- stopPropagation()
- target、currentTarget
- type、timeStamp
違い:
- イベント名がキャメルケース(onClick、onChangeなど)
- falseを返すだけではデフォルト動作を防げない
SyntheticEventを通じて、ネイティブイベントにもアクセスできますね。
基本的なイベントハンドラーの書き方
関数コンポーネントでのイベント処理
最もシンプルな例から見ていきましょう。
function Button() {
const handleClick = (event) => {
console.log('Button clicked!');
console.log('Event type:', event.type);
};
return <button onClick={handleClick}>Click me</button>;
}
handleClick関数の第一引数として、イベントオブジェクトが自動的に渡されます。
インラインでの記述
簡単な処理なら、直接書くこともできます。
function Button() {
return (
<button onClick={(event) => {
console.log('Clicked!');
}}>
Click me
</button>
);
}
ただし、複雑な処理は別の関数に切り出す方が読みやすいですね。
引数を渡したい場合
イベントオブジェクト以外のデータも渡せます。
function ItemList() {
const handleItemClick = (itemId, event) => {
console.log('Item ID:', itemId);
console.log('Event:', event);
};
return (
<div>
<button onClick={(e) => handleItemClick(1, e)}>Item 1</button>
<button onClick={(e) => handleItemClick(2, e)}>Item 2</button>
</div>
);
}
アロー関数で包むことで、追加の引数を渡せます。
よく使うイベントの種類
クリックイベント
最も基本的なイベントです。
function ClickExample() {
const handleClick = (event) => {
console.log('Clicked element:', event.target);
};
const handleDoubleClick = (event) => {
console.log('Double clicked!');
};
return (
<div>
<button onClick={handleClick}>Single Click</button>
<button onDoubleClick={handleDoubleClick}>Double Click</button>
</div>
);
}
onClickは1回のクリック、onDoubleClickはダブルクリックに反応しますね。
フォーム関連イベント
ユーザー入力を扱う重要なイベントです。
function FormExample() {
const handleChange = (event) => {
console.log('Input value:', event.target.value);
};
const handleSubmit = (event) => {
event.preventDefault(); // フォーム送信を防ぐ
console.log('Form submitted!');
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={handleChange}
placeholder="Type something"
/>
<button type="submit">Submit</button>
</form>
);
}
onChangeは入力のたびに発火し、onSubmitはフォーム送信時に実行されます。
キーボードイベント
キー入力を検知するイベントです。
function KeyboardExample() {
const handleKeyDown = (event) => {
console.log('Key pressed:', event.key);
if (event.key === 'Enter') {
console.log('Enter key pressed!');
}
};
return (
<input
type="text"
onKeyDown={handleKeyDown}
placeholder="Press Enter"
/>
);
}
onKeyDown、onKeyUp、onKeyPress(非推奨)が使えますよ。
マウスイベント
マウスの動きを追跡できます。
function MouseExample() {
const handleMouseEnter = () => {
console.log('Mouse entered!');
};
const handleMouseLeave = () => {
console.log('Mouse left!');
};
return (
<div
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
style={{ padding: '20px', background: 'lightblue' }}
>
Hover over me
</div>
);
}
ホバー効果やツールチップの実装に便利ですね。
イベントオブジェクトの主要プロパティ
event.target
実際にイベントが発生した要素を指します。
function TargetExample() {
const handleClick = (event) => {
console.log('Clicked element:', event.target);
console.log('Element tag:', event.target.tagName);
console.log('Element text:', event.target.textContent);
};
return (
<div onClick={handleClick}>
<button>Button 1</button>
<button>Button 2</button>
</div>
);
}
どのボタンがクリックされたか、正確に取得できます。
event.currentTarget
イベントハンドラーが設定されている要素を指します。
function CurrentTargetExample() {
const handleClick = (event) => {
console.log('target:', event.target.tagName);
console.log('currentTarget:', event.currentTarget.tagName);
};
return (
<div onClick={handleClick}>
<span>Click me</span>
</div>
);
}
spanをクリックすると、targetはSPAN、currentTargetはDIVになりますね。
event.type
発生したイベントの種類を示します。
function TypeExample() {
const handleEvent = (event) => {
console.log('Event type:', event.type);
};
return (
<button
onClick={handleEvent}
onMouseEnter={handleEvent}
onFocus={handleEvent}
>
Interact with me
</button>
);
}
1つのハンドラーで複数のイベントを処理する際に便利です。
event.nativeEvent
元のブラウザイベントにアクセスできます。
function NativeEventExample() {
const handleClick = (event) => {
console.log('Synthetic event:', event);
console.log('Native event:', event.nativeEvent);
};
return <button onClick={handleClick}>Click me</button>;
}
特殊な処理が必要な場合のみ使用しましょう。
重要なメソッド
preventDefault()
要素のデフォルト動作を防ぎます。
function PreventDefaultExample() {
const handleSubmit = (event) => {
event.preventDefault();
console.log('Form submission prevented!');
// ここで独自の処理を実行
};
const handleLinkClick = (event) => {
event.preventDefault();
console.log('Link navigation prevented!');
};
return (
<div>
<form onSubmit={handleSubmit}>
<input type="text" />
<button type="submit">Submit</button>
</form>
<a href="https://example.com" onClick={handleLinkClick}>
Click me (won't navigate)
</a>
</div>
);
}
フォーム送信やリンク遷移を制御できますね。
stopPropagation()
イベントの伝播(バブリング)を止めます。
function StopPropagationExample() {
const handleParentClick = () => {
console.log('Parent clicked!');
};
const handleChildClick = (event) => {
event.stopPropagation();
console.log('Child clicked! (parent will not be triggered)');
};
return (
<div onClick={handleParentClick} style={{ padding: '20px', background: 'lightblue' }}>
Parent
<button onClick={handleChildClick}>Child</button>
</div>
);
}
子要素のイベントが親に伝わらないようにできます。
フォーム入力の扱い方
制御されたコンポーネント
Reactの状態で入力値を管理する方法です。
import { useState } from 'react';
function ControlledInput() {
const [text, setText] = useState('');
const handleChange = (event) => {
setText(event.target.value);
};
return (
<div>
<input
type="text"
value={text}
onChange={handleChange}
/>
<p>You typed: {text}</p>
</div>
);
}
入力値が常にReactの状態と同期されます。
複数の入力フィールド
1つのハンドラーで複数の入力を管理できます。
import { useState } from 'react';
function MultipleInputs() {
const [formData, setFormData] = useState({
name: '',
email: '',
age: ''
});
const handleChange = (event) => {
const { name, value } = event.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
return (
<form>
<input
name="name"
value={formData.name}
onChange={handleChange}
placeholder="Name"
/>
<input
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Email"
/>
<input
name="age"
value={formData.age}
onChange={handleChange}
placeholder="Age"
/>
</form>
);
}
name属性を使って、どの入力が変更されたか判別できますね。
チェックボックスとラジオボタン
特別な扱いが必要です。
import { useState } from 'react';
function CheckboxExample() {
const [isChecked, setIsChecked] = useState(false);
const handleCheckboxChange = (event) => {
setIsChecked(event.target.checked); // valueではなくchecked
};
return (
<label>
<input
type="checkbox"
checked={isChecked}
onChange={handleCheckboxChange}
/>
{isChecked ? 'Checked' : 'Unchecked'}
</label>
);
}
チェックボックスはcheckedプロパティを使用します。
よくある間違いと解決方法
イベントハンドラーを実行してしまう
括弧を付けると、レンダリング時に実行されてしまいます。
// 間違い
<button onClick={handleClick()}>Click me</button>
// 正しい
<button onClick={handleClick}>Click me</button>
// 引数を渡す場合
<button onClick={() => handleClick(id)}>Click me</button>
関数の参照を渡すか、アロー関数で包みましょう。
thisのバインディング(クラスコンポーネント)
クラスコンポーネントでは、thisのバインディングが必要でした。
class Button extends React.Component {
constructor(props) {
super(props);
// コンストラクタでバインド
this.handleClick = this.handleClick.bind(this);
}
handleClick(event) {
console.log(this.props);
}
render() {
return <button onClick={this.handleClick}>Click me</button>;
}
}
関数コンポーネントとアロー関数を使えば、この問題は発生しませんよ。
イベントオブジェクトが消える
React 17以前では、イベントプーリングにより注意が必要でした。
// React 16以前の問題
function OldPoolingIssue() {
const handleClick = (event) => {
setTimeout(() => {
// エラー:イベントオブジェクトが再利用済み
console.log(event.target);
}, 100);
};
return <button onClick={handleClick}>Click me</button>;
}
// 解決方法1:必要な値を保存
function Solution1() {
const handleClick = (event) => {
const target = event.target;
setTimeout(() => {
console.log(target); // OK
}, 100);
};
return <button onClick={handleClick}>Click me</button>;
}
// 解決方法2:persist()を呼ぶ(React 16)
function Solution2() {
const handleClick = (event) => {
event.persist();
setTimeout(() => {
console.log(event.target); // OK
}, 100);
};
return <button onClick={handleClick}>Click me</button>;
}
React 17以降では、イベントプーリングが廃止され、この問題は解消されています。
パフォーマンス最適化
useCallbackでメモ化
関数を再作成しないようにできます。
import { useState, useCallback } from 'react';
function OptimizedComponent() {
const [count, setCount] = useState(0);
const handleClick = useCallback((event) => {
console.log('Button clicked!');
setCount(prev => prev + 1);
}, []); // 依存配列が空なので、関数は再作成されない
return <button onClick={handleClick}>Count: {count}</button>;
}
子コンポーネントへの不要な再レンダリングを防げますね。
イベントデリゲーション
親要素で一括してイベントを処理する方法です。
function EventDelegation() {
const handleListClick = (event) => {
if (event.target.tagName === 'LI') {
console.log('Item clicked:', event.target.textContent);
}
};
return (
<ul onClick={handleListClick}>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
);
}
個別にハンドラーを設定するより、メモリ効率が良くなります。
TypeScriptでの型定義
イベントハンドラーの型
適切な型を指定することで、安全性が向上します。
import React from 'react';
function TypedComponent() {
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
console.log(event.currentTarget);
};
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
console.log(event.target.value);
};
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
};
return (
<div>
<button onClick={handleClick}>Click</button>
<input onChange={handleChange} />
<form onSubmit={handleSubmit}>
<button type="submit">Submit</button>
</form>
</div>
);
}
要素の型を指定することで、自動補完も効きますよ。
よく使う型一覧
主要なイベント型をまとめました。
// マウスイベント
React.MouseEvent<HTMLElement>
// キーボードイベント
React.KeyboardEvent<HTMLElement>
// フォームイベント
React.FormEvent<HTMLFormElement>
React.ChangeEvent<HTMLInputElement>
React.ChangeEvent<HTMLTextAreaElement>
React.ChangeEvent<HTMLSelectElement>
// フォーカスイベント
React.FocusEvent<HTMLElement>
// クリップボードイベント
React.ClipboardEvent<HTMLElement>
// ドラッグイベント
React.DragEvent<HTMLElement>
状況に応じて使い分けましょう。
カスタムイベントの作成
子から親へのデータ送信
コールバック関数を使った一般的なパターンです。
function Parent() {
const handleChildEvent = (data) => {
console.log('Received from child:', data);
};
return <Child onCustomEvent={handleChildEvent} />;
}
function Child({ onCustomEvent }) {
const handleClick = () => {
onCustomEvent({ message: 'Hello from child!', timestamp: Date.now() });
};
return <button onClick={handleClick}>Send to Parent</button>;
}
カスタムデータを親コンポーネントに渡せますね。
複数のイベントハンドラー
1つの要素に複数のイベントを設定できます。
function MultipleHandlers() {
const handleClick = () => console.log('Clicked!');
const handleMouseEnter = () => console.log('Mouse entered!');
const handleMouseLeave = () => console.log('Mouse left!');
return (
<button
onClick={handleClick}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
Hover and Click
</button>
);
}
実践的なパターン
debounce(デバウンス)
連続したイベントを制御します。
import { useState, useCallback } from 'react';
function SearchInput() {
const [searchTerm, setSearchTerm] = useState('');
const debounce = (func, delay) => {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func(...args), delay);
};
};
const performSearch = useCallback(
debounce((value) => {
console.log('Searching for:', value);
// API呼び出しなど
}, 500),
[]
);
const handleChange = (event) => {
const value = event.target.value;
setSearchTerm(value);
performSearch(value);
};
return (
<input
type="text"
value={searchTerm}
onChange={handleChange}
placeholder="Search..."
/>
);
}
入力のたびにAPIを呼ぶのではなく、入力が止まってから実行できますね。
フォームバリデーション
リアルタイムでバリデーションを実行します。
import { useState } from 'react';
function ValidationForm() {
const [email, setEmail] = useState('');
const [error, setError] = useState('');
const validateEmail = (value) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(value)) {
setError('Invalid email format');
} else {
setError('');
}
};
const handleChange = (event) => {
const value = event.target.value;
setEmail(value);
validateEmail(value);
};
return (
<div>
<input
type="email"
value={email}
onChange={handleChange}
placeholder="Enter email"
/>
{error && <p style={{ color: 'red' }}>{error}</p>}
</div>
);
}
ユーザーにすぐにフィードバックを返せます。
まとめ:Reactイベントオブジェクトをマスターしよう
Reactのイベントオブジェクトを理解することで、インタラクティブなUIを作れます。
基本的な使い方から、パフォーマンス最適化まで押さえておきましょう。
この記事の重要ポイント:
- SyntheticEventはブラウザ間の違いを吸収する
- event.targetとevent.currentTargetの違いを理解する
- preventDefault()でデフォルト動作を防ぐ
- stopPropagation()でイベント伝播を制御
- 制御されたコンポーネントで入力を管理
- TypeScriptで型安全性を確保
- useCallbackでパフォーマンスを最適化
- debounceで連続イベントを制御
- React 17以降はイベントプーリングが廃止された
まずは簡単なボタンのクリックイベントから始めてみましょう。
実際にコードを書きながら学ぶことで、イベント処理が自然と身についていきますよ。


コメント