イベントハンドリング
ユーザーがボタンをクリックしたり、フォームに入力したりする操作に対して応答する仕組みがイベントハンドリングです。 React のイベント処理は HTML の標準的なイベントと似ていますが、いくつかの重要な違いがあります。 この章では、React でのイベント処理の基本から実践的なパターンまでを解説します。
React のイベント処理
React は、ブラウザのネイティブイベントをラップした SyntheticEvent(合成イベント) を使用します。 これにより、ブラウザ間の差異を吸収し、すべてのブラウザで一貫した動作を実現しています。
React のイベント処理と HTML のイベント処理の主な違いは以下の通りです。
| 項目 | HTML | React |
|---|---|---|
| 命名規則 | 小文字(onclick) |
キャメルケース(onClick) |
| ハンドラの指定 | 文字列("handleClick()") |
関数の参照({handleClick}) |
| デフォルト動作の阻止 | return false が使える |
event.preventDefault() を明示的に呼ぶ |
| イベントオブジェクト | ネイティブ Event | SyntheticEvent(ラッパー) |
<!-- HTML -->
<button onclick="handleClick()">クリック</button>
{/* React */}
<button onClick={handleClick}>クリック</button>
React のイベント名はキャメルケースです。onclick ではなく onClick、
onchange ではなく onChange、onsubmit ではなく onSubmit のように記述します。
onClick イベント
最も基本的なイベントハンドラが onClick です。
ボタンやリンクなどのクリック操作に対して処理を実行します。
import { useState } from 'react';
function ClickExample() {
const [message, setMessage] = useState('ボタンを押してください');
function handleClick() {
setMessage('ボタンが押されました!');
}
return (
<div>
<p>{message}</p>
<button onClick={handleClick}>クリック</button>
</div>
);
}
export default ClickExample;
注意:onClick={handleClick()} のように括弧を付けると、
レンダリング時に関数がすぐに実行されてしまいます。
onClick={handleClick} のように関数の参照を渡してください。
イベントハンドラの書き方
イベントハンドラの定義にはいくつかのスタイルがあります。
1. 関数宣言(推奨)
function MyComponent() {
function handleClick() {
console.log('クリックされました');
}
return <button onClick={handleClick}>ボタン</button>;
}
2. アロー関数での定義
function MyComponent() {
const handleClick = () => {
console.log('クリックされました');
};
return <button onClick={handleClick}>ボタン</button>;
}
3. インライン(簡単な処理向け)
function MyComponent() {
return (
<button onClick={() => console.log('クリックされました')}>
ボタン
</button>
);
}
命名規則:
イベントハンドラの名前は handle + イベント名にするのが慣習です。
例えば、クリックなら handleClick、フォーム送信なら handleSubmit、
入力変更なら handleChange のように命名しましょう。
Props として渡す場合は on + イベント名(例: onDelete、onSave)が一般的です。
イベントオブジェクト
イベントハンドラの第一引数には、SyntheticEvent オブジェクトが渡されます。 このオブジェクトは、ブラウザのネイティブイベントと同じインターフェースを持っています。
function EventObject() {
function handleClick(event) {
console.log('イベントタイプ:', event.type); // "click"
console.log('ターゲット要素:', event.target); // クリックされた DOM 要素
console.log('ターゲットのテキスト:', event.target.textContent);
}
return <button onClick={handleClick}>情報を表示</button>;
}
preventDefault() でデフォルト動作を阻止
リンクのページ遷移やフォームの送信など、ブラウザのデフォルト動作を阻止するには
event.preventDefault() を呼び出します。
function CustomLink() {
function handleClick(event) {
event.preventDefault(); // ページ遷移を阻止
console.log('リンクがクリックされましたが遷移しません');
}
return (
<a href="https://example.com" onClick={handleClick}>
このリンクは遷移しません
</a>
);
}
引数付きイベントハンドラ
イベントハンドラに追加の引数を渡したい場合は、アロー関数でラップします。 リスト内のアイテムを削除する場合など、ID を渡す必要がある場面でよく使われます。
import { useState } from 'react';
function ItemList() {
const [items, setItems] = useState([
{ id: 1, name: 'りんご' },
{ id: 2, name: 'バナナ' },
{ id: 3, name: 'みかん' },
]);
function handleDelete(id) {
setItems(items.filter(item => item.id !== id));
}
return (
<ul>
{items.map(item => (
<li key={item.id}>
{item.name}
<button onClick={() => handleDelete(item.id)}>
削除
</button>
</li>
))}
</ul>
);
}
export default ItemList;
onClick={() => handleDelete(item.id)} のようにアロー関数で包むことで、
クリック時に handleDelete が item.id を引数として受け取ります。
onChange イベント
onChange は、入力フィールドの値が変わったときに発火するイベントです。
React では、入力フィールドの値を State で管理する「制御コンポーネント(Controlled Component)」パターンが基本です。
import { useState } from 'react';
function ControlledInput() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
return (
<div>
<label>
名前:
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</label>
<label>
メール:
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</label>
<p>入力内容: {name} ({email})</p>
</div>
);
}
export default ControlledInput;
複数の入力をまとめて管理
入力フィールドが多い場合は、1 つのオブジェクト State にまとめると管理しやすくなります。
name 属性を使って、どのフィールドが変更されたかを判別します。
import { useState } from 'react';
function MultiInput() {
const [form, setForm] = useState({
name: '',
email: '',
message: '',
});
function handleChange(e) {
const { name, value } = e.target;
setForm(prev => ({
...prev,
[name]: value,
}));
}
return (
<form>
<input name="name" value={form.name} onChange={handleChange} />
<input name="email" value={form.email} onChange={handleChange} />
<textarea name="message" value={form.message} onChange={handleChange} />
</form>
);
}
export default MultiInput;
計算プロパティ名:
[name]: value は JavaScript の計算プロパティ名(computed property name)構文です。
name 変数の値がオブジェクトのキーとして使われるため、
1 つの handleChange 関数で複数の入力フィールドを処理できます。
onSubmit イベント
フォーム送信を処理するには onSubmit イベントを使います。
フォームのデフォルト動作(ページのリロード)を防ぐため、必ず event.preventDefault() を呼び出します。
import { useState } from 'react';
function LoginForm() {
const [form, setForm] = useState({ username: '', password: '' });
const [error, setError] = useState('');
function handleChange(e) {
const { name, value } = e.target;
setForm(prev => ({ ...prev, [name]: value }));
}
function handleSubmit(event) {
event.preventDefault(); // ページリロードを阻止
if (!form.username || !form.password) {
setError('すべての項目を入力してください');
return;
}
console.log('送信データ:', form);
setError('');
// ここで API 通信などを行う
}
return (
<form onSubmit={handleSubmit}>
{error && <p className="error">{error}</p>}
<label>
ユーザー名:
<input
name="username"
value={form.username}
onChange={handleChange}
/>
</label>
<label>
パスワード:
<input
type="password"
name="password"
value={form.password}
onChange={handleChange}
/>
</label>
<button type="submit">ログイン</button>
</form>
);
}
export default LoginForm;
イベントの伝播
DOM では、イベントは子要素から親要素へと伝播(バブリング)します。
React でもこの仕組みは同じです。親要素のイベントハンドラが意図せず呼ばれてしまう場合は、
event.stopPropagation() で伝播を停止できます。
function Propagation() {
function handleOuterClick() {
console.log('外側がクリックされました');
}
function handleInnerClick(event) {
event.stopPropagation(); // 伝播を停止
console.log('内側がクリックされました');
}
return (
<div onClick={handleOuterClick} style={{ padding: '20px', background: '#eee' }}>
<p>外側の領域</p>
<button onClick={handleInnerClick}>
内側のボタン
</button>
</div>
);
}
上の例で、内側のボタンをクリックすると stopPropagation() によって
外側の handleOuterClick は呼ばれません。
もし stopPropagation() を外すと、ボタンをクリックしたときに
handleInnerClick と handleOuterClick の両方が実行されます。
preventDefault vs stopPropagation:
preventDefault() はブラウザのデフォルト動作(リンク遷移、フォーム送信など)を阻止します。
stopPropagation() はイベントが親要素に伝播するのを阻止します。
両者は目的が異なるため、混同しないようにしましょう。
よく使うイベント一覧
React で頻繁に使用するイベントの一覧です。
| イベント名 | 発火タイミング | 主な用途 |
|---|---|---|
onClick |
要素がクリックされたとき | ボタン、リンクの操作 |
onChange |
入力値が変更されたとき | テキスト入力、セレクトボックス、チェックボックス |
onSubmit |
フォームが送信されたとき | フォームの送信処理 |
onFocus |
要素にフォーカスが当たったとき | 入力フィールドのハイライト表示 |
onBlur |
要素からフォーカスが外れたとき | 入力値のバリデーション |
onKeyDown |
キーが押されたとき | キーボードショートカット、Enter キーでの送信 |
onKeyUp |
キーが離されたとき | キー入力の検知 |
onMouseEnter |
マウスが要素に入ったとき | ホバー効果、ツールチップ表示 |
onMouseLeave |
マウスが要素から出たとき | ホバー効果の解除 |
onScroll |
スクロールされたとき | 無限スクロール、スクロール位置の追跡 |
onDoubleClick |
ダブルクリックされたとき | 編集モードの切り替え |
onCopy |
コピー操作時 | コピー操作のカスタマイズ |
onDrag / onDrop |
ドラッグ & ドロップ操作時 | ファイルアップロード、並び替え |
よく使うキーボードイベントの例
function EnterToSubmit() {
function handleKeyDown(event) {
if (event.key === 'Enter') {
console.log('Enter キーが押されました');
// 検索やメッセージ送信などを実行
}
}
return (
<input
type="text"
placeholder="Enter で送信"
onKeyDown={handleKeyDown}
/>
);
}
まとめ:
React のイベントハンドリングの基本は、(1) キャメルケースのイベント名を使う、
(2) 関数の参照を渡す(括弧を付けない)、(3) 必要に応じて preventDefault() や
stopPropagation() を呼ぶ、の 3 点です。
ハンドラの命名は handle~、Props として渡す場合は on~ を慣習として覚えておきましょう。