はじめに
Reactを学び始めた方が最初に戸惑うポイントの一つが、イベントハンドラーの書き方です。通常のJavaScriptでのDOM操作とは異なる記法や考え方が必要になります。
この記事では、Reactにおけるイベントハンドラーの基本から実践的な使い方まで、通常のJavaScriptとの違いを比較しながら解説していきます。
通常のJavaScriptとReactのイベントハンドラーの違い
まずは、通常のJavaScriptとReactでのイベントハンドラーの設定方法を比較してみましょう。
記法の違い
通常のJavaScript
// HTML
<button id="myButton">クリック</button>
// JavaScript
const button = document.getElementById('myButton');
button.addEventListener('click', function() {
console.log('クリックされました');
});
// または
<button onclick="handleClick()">クリック</button>
React
function App() {
const handleClick = () => {
console.log('クリックされました');
};
return (
<button onClick={handleClick}>クリック</button>
);
}
Reactでは、JSX内に直接イベントハンドラーを記述するため、addEventListenerのようなメソッドは使いません。また、HTMLの属性として記述する場合と異なり、文字列ではなく関数を渡します。
イベント名の違い(キャメルケース)
通常のJavaScriptではイベント名が小文字ですが、Reactではキャメルケースで記述します。
| 通常のJavaScript | React |
|---|---|
| onclick | onClick |
| onchange | onChange |
| onsubmit | onSubmit |
| onmouseover | onMouseOver |
| onkeydown | onKeyDown |
// 正しい書き方
<button onClick={handleClick}>クリック</button>
// 間違った書き方(動作しません)
<button onclick={handleClick}>クリック</button>
イベント伝播の扱い
デフォルト動作を防ぐ方法も異なりますね。
通常のJavaScript
element.addEventListener('click', function(e) {
e.preventDefault();
return false; // これも使える
});
React
const handleSubmit = (e) => {
e.preventDefault(); // これが必要
};
<form onSubmit={handleSubmit}>
{/* フォーム内容 */}
</form>
Reactではreturn falseによるデフォルト動作の防止は機能しないため、必ずe.preventDefault()を使用します。
thisの扱い方
クラスコンポーネントを使う場合、thisのバインディングに注意が必要です。ただし、関数コンポーネントとフックが主流の現在では、この問題はほとんど発生しません。
// クラスコンポーネントの例(参考)
class MyComponent extends React.Component {
constructor(props) {
super(props);
// bindが必要
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(this); // bindしないとundefinedになる
}
render() {
return <button onClick={this.handleClick}>クリック</button>;
}
}
// 関数コンポーネントではthisの問題は発生しない
function MyComponent() {
const handleClick = () => {
console.log('クリックされました');
};
return <button onClick={handleClick}>クリック</button>;
}
Reactでのイベントハンドラーの基本的な書き方
関数コンポーネントでのイベントハンドラーの書き方を見ていきましょう。
関数コンポーネントでの実装
最も基本的な実装方法は、コンポーネント内で関数を定義し、それをJSX内で参照する形です。
function Counter() {
const [count, setCount] = useState(0);
const handleIncrement = () => {
setCount(count + 1);
};
return (
<div>
<p>カウント: {count}</p>
<button onClick={handleIncrement}>増やす</button>
</div>
);
}
インラインでの記述
シンプルな処理であれば、インラインで記述することもできます。
function Greeting() {
return (
<button onClick={() => alert('こんにちは!')}>
挨拶する
</button>
);
}
ただし、インラインで記述すると、レンダリングのたびに新しい関数が生成されるため、パフォーマンスが重要な場面では避けた方が良いでしょう。
関数を定義する方法
複数の書き方がありますが、アロー関数を使うのが一般的です。
function Example() {
// アロー関数(推奨)
const handleClick1 = () => {
console.log('クリック1');
};
// 通常の関数
function handleClick2() {
console.log('クリック2');
}
return (
<>
<button onClick={handleClick1}>ボタン1</button>
<button onClick={handleClick2}>ボタン2</button>
</>
);
}
よく使うイベントハンドラーの例
実際によく使用するイベントハンドラーを見ていきましょう。
onClick(クリックイベント)
ボタンのクリックや要素のクリックを検知します。
function ClickExample() {
const [message, setMessage] = useState('');
const handleClick = () => {
setMessage('ボタンがクリックされました');
};
return (
<div>
<button onClick={handleClick}>クリック</button>
<p>{message}</p>
</div>
);
}
onChange(入力イベント)
入力フィールドの値が変更されたときに発火します。フォームの制御でよく使いますね。
function InputExample() {
const [text, setText] = useState('');
const handleChange = (e) => {
setText(e.target.value);
};
return (
<div>
<input
type="text"
value={text}
onChange={handleChange}
/>
<p>入力内容: {text}</p>
</div>
);
}
onSubmit(フォーム送信)
フォームの送信時に発火します。必ずpreventDefault()でデフォルトの送信動作を防ぎます。
function FormExample() {
const [name, setName] = useState('');
const handleSubmit = (e) => {
e.preventDefault(); // ページリロードを防ぐ
console.log('送信された名前:', name);
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<button type="submit">送信</button>
</form>
);
}
イベントオブジェクト(SyntheticEvent)の扱い方
Reactのイベントハンドラーに渡されるイベントオブジェクトは、ブラウザのネイティブイベントをラップしたSyntheticEvent(合成イベント)と呼ばれるものです。
イベントオブジェクトとは
SyntheticEventは、ブラウザ間の違いを吸収し、一貫したインターフェースを提供してくれます。
function EventExample() {
const handleClick = (e) => {
console.log(e); // SyntheticEventオブジェクト
console.log(e.nativeEvent); // 元のブラウザイベント
};
return <button onClick={handleClick}>クリック</button>;
}
よく使うプロパティとメソッド
function DetailedEventExample() {
const handleEvent = (e) => {
// イベントの種類
console.log(e.type); // 'click', 'change'など
// イベントが発生した要素
console.log(e.target); // イベントが発生したDOM要素
console.log(e.currentTarget); // イベントハンドラーが設定された要素
// 入力値の取得
console.log(e.target.value); // input要素の値
// デフォルト動作の防止
e.preventDefault();
// イベント伝播の停止
e.stopPropagation();
};
return (
<input
type="text"
onChange={handleEvent}
/>
);
}
主要なプロパティとメソッド
| プロパティ/メソッド | 説明 |
|---|---|
| e.target | イベントが発生した要素 |
| e.currentTarget | イベントハンドラーが設定された要素 |
| e.target.value | input要素などの値 |
| e.preventDefault() | デフォルト動作を防ぐ |
| e.stopPropagation() | イベントの伝播を止める |
| e.type | イベントの種類 |
イベントハンドラーに引数を渡す方法
イベントハンドラーに追加の引数を渡したい場合の方法を見ていきましょう。
アロー関数を使う方法
最も一般的で分かりやすい方法です。
function ListExample() {
const items = ['りんご', 'バナナ', 'オレンジ'];
const handleClick = (item) => {
console.log(`${item}がクリックされました`);
};
return (
<ul>
{items.map((item, index) => (
<li key={index}>
<button onClick={() => handleClick(item)}>
{item}
</button>
</li>
))}
</ul>
);
}
イベントオブジェクトも一緒に使いたい場合は、明示的に渡します。
function DetailedListExample() {
const handleClick = (item, e) => {
console.log('クリックされたアイテム:', item);
console.log('イベントオブジェクト:', e);
};
return (
<button onClick={(e) => handleClick('りんご', e)}>
クリック
</button>
);
}
bind()を使う方法
bind()メソッドを使う方法もあります。ただし、現在ではあまり使われません。
function BindExample() {
const handleClick = (item, e) => {
console.log('アイテム:', item);
console.log('イベント:', e);
};
return (
<button onClick={handleClick.bind(null, 'りんご')}>
クリック
</button>
);
}
アロー関数の方が読みやすいため、特別な理由がない限りアロー関数を使うことをおすすめします。
よくあるミスと注意点
初心者がよくやってしまうミスと、その対処法を見ていきましょう。
関数を実行してしまう間違い
イベントハンドラーには関数の参照を渡す必要がありますが、誤って関数を実行してしまうケースがあります。
function MistakeExample() {
const handleClick = () => {
console.log('クリックされました');
};
return (
<div>
{/* 正しい: 関数の参照を渡す */}
<button onClick={handleClick}>正しい</button>
{/* 間違い: 関数を実行している(レンダリング時に実行される) */}
<button onClick={handleClick()}>間違い</button>
{/* 引数を渡す場合はアロー関数で囲む */}
<button onClick={() => handleClick()}>これは正しい</button>
</div>
);
}
onClick={handleClick()}と書くと、レンダリング時に関数が実行され、その返り値がイベントハンドラーとして設定されてしまいます。クリック時ではなく、画面表示時に実行されてしまうので注意が必要です。
無限ループに注意
状態更新とイベントハンドラーの組み合わせで、無限ループが発生することがあります。
function InfiniteLoopExample() {
const [count, setCount] = useState(0);
// 間違い: レンダリングのたびにsetCountが実行される
return (
<div>
<p>{count}</p>
<button onClick={setCount(count + 1)}>間違い</button>
</div>
);
}
function CorrectExample() {
const [count, setCount] = useState(0);
// 正しい: クリック時のみ実行される
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>正しい</button>
{/* または */}
<button onClick={() => setCount(prev => prev + 1)}>これも正しい</button>
</div>
);
}
よくあるミスのまとめ
| 間違った書き方 | 正しい書き方 | 説明 |
|---|---|---|
onClick={handleClick()} |
onClick={handleClick} |
関数の参照を渡す |
onClick={setCount(count + 1)} |
onClick={() => setCount(count + 1)} |
アロー関数で囲む |
onclick={handleClick} |
onClick={handleClick} |
キャメルケースで書く |
まとめ
Reactのイベントハンドラーについて、以下のポイントを押さえておきましょう。
通常のJavaScriptとの主な違い
- JSX内に直接記述する
- イベント名はキャメルケースで書く
- 関数の参照を渡す(文字列ではない)
-
return falseは機能しない
基本的な使い方
- コンポーネント内で関数を定義してJSXで参照
- インライン記述も可能だが、パフォーマンスに注意
- イベントオブジェクトは自動的に渡される
注意すべき点
-
onClick={handleClick()}のように関数を実行しない - 引数を渡す場合はアロー関数で囲む
-
preventDefault()でデフォルト動作を防ぐ
これらの基本をマスターすれば、ReactでのインタラクティブなUIを作成できるようになります。最初は戸惑うかもしれませんが、慣れれば通常のJavaScriptよりも直感的に書けるようになりますよ。