Webサイトでボタンをクリックしたり、フォームに入力したり。ユーザーの操作に反応してページが変わる、そんな体験は当たり前になっていますよね。
でも、Reactでそれを実装しようとすると、「イベントってどう書くの?」「普通のJavaScriptと何が違うの?」と戸惑う方も多いはずです。
この記事では、Reactにおけるイベント処理について、基礎から実践的なテクニックまで、初心者の方にも分かりやすく解説していきます。
クリック、入力、送信など、あらゆるユーザー操作に対応できるようになりますよ。
Reactのイベントとは
イベントの基本概念
イベントとは、ユーザーがWebページ上で起こすアクション(操作)のことです。
代表的なイベント
- クリック(ボタンを押す)
- 入力(テキストを入力する)
- マウス移動(カーソルを動かす)
- キーボード操作(キーを押す)
- フォーム送信(送信ボタンを押す)
これらの操作に反応して、何かを実行するのが「イベント処理」です。
ReactとJavaScriptのイベントの違い
Reactのイベント処理は、標準のJavaScriptと似ていますが、いくつか違いがあります。
命名規則の違い
// 標準のHTML/JavaScript
<button onclick="handleClick()">クリック</button>
// React
<button onClick={handleClick}>クリック</button>
Reactでは「キャメルケース」(先頭は小文字、単語の区切りは大文字)を使います。
イベントハンドラーの指定方法
Reactでは、文字列ではなく関数そのものを渡します。中括弧 {}
で囲むのがポイント。
イベントオブジェクトの扱い
Reactは「合成イベント(SyntheticEvent)」という独自のラッパーを使います。ブラウザ間の違いを吸収してくれるので、互換性の心配が減ります。
基本的なイベント処理の書き方
クリックイベントの実装
最もシンプルな例から始めましょう。
基本的な書き方
function ClickButton() {
function handleClick() {
alert('ボタンがクリックされました!');
}
return (
<button onClick={handleClick}>
クリックしてね
</button>
);
}
handleClick
という関数を定義して、onClick
に渡しています。
注意:関数は実行しない
// ❌ 間違い(すぐに実行されてしまう)
<button onClick={handleClick()}>クリック</button>
// ✅ 正しい(関数そのものを渡す)
<button onClick={handleClick}>クリック</button>
括弧 ()
を付けると、レンダリング時に実行されてしまいます。
インライン関数の使用
簡単な処理なら、その場で関数を定義することもできます。
アロー関数を使う
function InlineButton() {
return (
<button onClick={() => alert('クリックされた!')}>
クリック
</button>
);
}
短い処理なら、この書き方もスッキリします。
イベントオブジェクトの取得
イベントハンドラーは、自動的にイベントオブジェクトを受け取ります。
イベントオブジェクトの使用例
function EventButton() {
function handleClick(event) {
console.log('イベントタイプ:', event.type);
console.log('クリックされた要素:', event.target);
}
return (
<button onClick={handleClick}>
情報を表示
</button>
);
}
event
パラメータには、クリック位置や対象要素など、様々な情報が含まれています。
よく使うイベントの種類
マウスイベント
マウス操作に関するイベントです。
onClick – クリック
<button onClick={handleClick}>クリック</button>
要素がクリックされたとき。
onDoubleClick – ダブルクリック
<div onDoubleClick={handleDoubleClick}>
ダブルクリックしてね
</div>
要素が2回連続でクリックされたとき。
onMouseEnter / onMouseLeave – マウスの出入り
function HoverDiv() {
function handleMouseEnter() {
console.log('マウスが入ってきた');
}
function handleMouseLeave() {
console.log('マウスが出ていった');
}
return (
<div
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
ここにマウスを持ってきて
</div>
);
}
ホバー効果を作るのに便利です。
フォーム関連イベント
入力欄やフォームで使うイベントです。
onChange – 値の変更
function InputField() {
function handleChange(event) {
console.log('入力された値:', event.target.value);
}
return (
<input
type="text"
onChange={handleChange}
placeholder="何か入力してね"
/>
);
}
テキスト入力、チェックボックス、セレクトボックスなど、値が変わったときに発火します。
onSubmit – フォーム送信
function LoginForm() {
function handleSubmit(event) {
event.preventDefault(); // ページ遷移を防ぐ
console.log('フォームが送信されました');
}
return (
<form onSubmit={handleSubmit}>
<input type="text" name="username" />
<button type="submit">送信</button>
</form>
);
}
event.preventDefault()
は重要。これがないとページがリロードされてしまいます。
onFocus / onBlur – フォーカスの出入り
<input
onFocus={() => console.log('フォーカスされた')}
onBlur={() => console.log('フォーカスが外れた')}
/>
入力欄がアクティブになったとき、離れたとき。
キーボードイベント
キー操作に関するイベントです。
onKeyDown / onKeyUp – キーの押下
function KeyInput() {
function handleKeyDown(event) {
console.log('押されたキー:', event.key);
if (event.key === 'Enter') {
console.log('Enterキーが押されました');
}
}
return (
<input
type="text"
onKeyDown={handleKeyDown}
placeholder="キーを押してね"
/>
);
}
特定のキー(Enterキーなど)での処理に便利です。
Stateとイベントの連携
useStateと組み合わせる
Reactの真価は、Stateとイベントを組み合わせたときに発揮されます。
カウンターの例
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
function handleIncrement() {
setCount(count + 1);
}
function handleDecrement() {
setCount(count - 1);
}
return (
<div>
<p>カウント: {count}</p>
<button onClick={handleIncrement}>+1</button>
<button onClick={handleDecrement}>-1</button>
</div>
);
}
ボタンをクリックするたびに、Stateが更新されて画面が再レンダリングされます。
フォーム入力の管理
ユーザーの入力をStateで管理する「制御されたコンポーネント」のパターンです。
テキスト入力の管理
import { useState } from 'react';
function TextInput() {
const [text, setText] = useState('');
function handleChange(event) {
setText(event.target.value);
}
return (
<div>
<input
type="text"
value={text}
onChange={handleChange}
placeholder="名前を入力"
/>
<p>入力された名前: {text}</p>
</div>
);
}
入力値をリアルタイムで表示できています。
複数の入力項目の管理
オブジェクトを使って、複数の入力をまとめて管理できます。
フォーム全体の管理
import { useState } from 'react';
function RegistrationForm() {
const [formData, setFormData] = useState({
username: '',
email: '',
age: ''
});
function handleChange(event) {
const { name, value } = event.target;
setFormData({
...formData,
[name]: value
});
}
function handleSubmit(event) {
event.preventDefault();
console.log('送信データ:', formData);
}
return (
<form onSubmit={handleSubmit}>
<input
name="username"
value={formData.username}
onChange={handleChange}
placeholder="ユーザー名"
/>
<input
name="email"
value={formData.email}
onChange={handleChange}
placeholder="メールアドレス"
/>
<input
name="age"
type="number"
value={formData.age}
onChange={handleChange}
placeholder="年齢"
/>
<button type="submit">登録</button>
</form>
);
}
1つの handleChange
関数で、すべての入力を処理しています。
イベント処理の応用テクニック
イベントハンドラーに引数を渡す
ボタンごとに異なる値を渡したい場合があります。
アロー関数でラップする
function ButtonList() {
function handleClick(id) {
console.log('クリックされたID:', id);
}
return (
<div>
<button onClick={() => handleClick(1)}>ボタン1</button>
<button onClick={() => handleClick(2)}>ボタン2</button>
<button onClick={() => handleClick(3)}>ボタン3</button>
</div>
);
}
アロー関数で囲むことで、引数付きの関数を渡せます。
bindメソッドを使う方法
<button onClick={handleClick.bind(null, 1)}>ボタン1</button>
こちらの方法もありますが、アロー関数の方が直感的です。
イベントの伝播を制御
親子関係にある要素では、イベントが伝播します。
イベントバブリングの例
function BubbleExample() {
function handleParentClick() {
console.log('親要素がクリックされた');
}
function handleChildClick(event) {
event.stopPropagation(); // 伝播を止める
console.log('子要素がクリックされた');
}
return (
<div onClick={handleParentClick}>
<button onClick={handleChildClick}>
子要素のボタン
</button>
</div>
);
}
event.stopPropagation()
で、親要素へのイベント伝播を防げます。
デフォルト動作の防止
特定の要素は、デフォルトで特定の動作をします。
リンクのデフォルト動作を防ぐ
function CustomLink() {
function handleClick(event) {
event.preventDefault(); // ページ遷移を防ぐ
console.log('カスタム処理を実行');
}
return (
<a href="https://example.com" onClick={handleClick}>
クリックしても遷移しません
</a>
);
}
フォーム送信やリンククリックの動作を、自分で制御できます。
条件付きイベント処理
特定の条件でのみ処理を実行したい場合。
条件分岐の例
function ConditionalButton() {
const [isEnabled, setIsEnabled] = useState(true);
function handleClick() {
if (!isEnabled) {
console.log('ボタンが無効です');
return;
}
console.log('処理を実行');
}
return (
<div>
<button onClick={handleClick}>
実行
</button>
<button onClick={() => setIsEnabled(!isEnabled)}>
有効/無効を切り替え
</button>
</div>
);
}
パフォーマンスの最適化
useCallbackでメモ化
イベントハンドラーを毎回再作成しないようにする最適化です。
useCallbackの使用
import { useState, useCallback } from 'react';
function OptimizedCounter() {
const [count, setCount] = useState(0);
const handleIncrement = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, []); // 依存配列が空なので、関数は1回だけ作成される
return (
<div>
<p>カウント: {count}</p>
<button onClick={handleIncrement}>+1</button>
</div>
);
}
子コンポーネントに関数を渡す場合、この最適化が効果的です。
インライン関数の注意点
毎回新しい関数が作られるため、パフォーマンスに影響することがあります。
避けるべきパターン
// 大量のリスト項目で、各項目がこれを使うと遅くなる
{items.map(item => (
<Item
key={item.id}
onClick={() => handleClick(item.id)}
/>
))}
改善方法
function ItemContainer() {
function handleItemClick(id) {
console.log('クリックされたID:', id);
}
return (
<>
{items.map(item => (
<Item
key={item.id}
id={item.id}
onItemClick={handleItemClick}
/>
))}
</>
);
}
// 子コンポーネント側
function Item({ id, onItemClick }) {
return (
<div onClick={() => onItemClick(id)}>
項目 {id}
</div>
);
}
よくある間違いとその対策
間違い1:イベントハンドラーをすぐ実行してしまう
// ❌ 間違い
<button onClick={handleClick()}>クリック</button>
// ✅ 正しい
<button onClick={handleClick}>クリック</button>
括弧を付けると、レンダリング時に実行されます。
間違い2:thisの参照エラー
クラスコンポーネントでよくある問題ですが、関数コンポーネントでは起きません。
クラスコンポーネントの場合
class MyComponent extends React.Component {
handleClick() {
console.log(this); // undefinedになる
}
render() {
return <button onClick={this.handleClick}>クリック</button>;
}
}
解決策:アロー関数を使う
class MyComponent extends React.Component {
handleClick = () => {
console.log(this); // 正しく参照できる
}
render() {
return <button onClick={this.handleClick}>クリック</button>;
}
}
関数コンポーネントなら、この問題は発生しません。
間違い3:preventDefault の忘れ
フォーム送信で、ページがリロードされてしまう問題。
// ❌ 間違い(ページがリロードされる)
function Form() {
function handleSubmit() {
console.log('送信');
}
return <form onSubmit={handleSubmit}>...</form>;
}
// ✅ 正しい
function Form() {
function handleSubmit(event) {
event.preventDefault(); // これが必要
console.log('送信');
}
return <form onSubmit={handleSubmit}>...</form>;
}
実践的な例:Todoリスト
学んだ知識を活かして、シンプルなTodoリストを作ってみましょう。
完全なコード例
import { useState } from 'react';
function TodoList() {
const [todos, setTodos] = useState([]);
const [inputValue, setInputValue] = useState('');
function handleInputChange(event) {
setInputValue(event.target.value);
}
function handleSubmit(event) {
event.preventDefault();
if (inputValue.trim() === '') return;
const newTodo = {
id: Date.now(),
text: inputValue,
completed: false
};
setTodos([...todos, newTodo]);
setInputValue('');
}
function handleToggle(id) {
setTodos(todos.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
));
}
function handleDelete(id) {
setTodos(todos.filter(todo => todo.id !== id));
}
return (
<div>
<h2>Todoリスト</h2>
<form onSubmit={handleSubmit}>
<input
type="text"
value={inputValue}
onChange={handleInputChange}
placeholder="新しいタスクを入力"
/>
<button type="submit">追加</button>
</form>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => handleToggle(todo.id)}
/>
<span style={{
textDecoration: todo.completed ? 'line-through' : 'none'
}}>
{todo.text}
</span>
<button onClick={() => handleDelete(todo.id)}>
削除
</button>
</li>
))}
</ul>
</div>
);
}
このコードには、以下のイベント処理が含まれています。
- フォーム送信(onSubmit)
- テキスト入力(onChange)
- チェックボックスの切り替え(onChange)
- 削除ボタンのクリック(onClick)
まとめ:Reactイベント処理をマスターしよう
Reactのイベント処理について、基礎から実践まで解説してきました。
この記事のポイント
- Reactのイベントはキャメルケースで書く
- イベントハンドラーは関数そのものを渡す(実行しない)
- onClick、onChange、onSubmit など様々なイベントがある
- Stateとイベントを組み合わせて、インタラクティブなUIを作る
- event.preventDefault() でデフォルト動作を制御
- useCallback でパフォーマンス最適化
イベント処理は、ユーザーとアプリケーションをつなぐ架け橋です。
この知識があれば、クリック、入力、送信など、あらゆるユーザー操作に対応できます。
次のステップ
- 実際に手を動かして、小さなアプリを作ってみる
- イベントとStateの組み合わせに慣れる
- useEffectと組み合わせて、より複雑な処理に挑戦
Reactのイベント処理をマスターして、魅力的なインタラクティブアプリを作っていきましょう!
コメント