【React】イベントハンドラの書き方完全ガイド – onChange・onClickの使い方
はじめに
こんにちは、優也@月収100万を目指して月100時間勉強する男です。
前回の記事「【React基礎】useStateとは?初心者向けに分かりやすく解説」で、Reactの状態管理について学びました。今回は、ユーザーの操作に反応するためのイベントハンドラについて解説します。
ボタンをクリックしたり、入力フォームに文字を入力したり…これらのユーザー操作を処理するのがイベントハンドラです。Reactでインタラクティブなアプリを作るには必須の知識です。
この記事では、onClick、onChange、onSubmitなど、よく使うイベントハンドラの書き方を、初心者でも分かるように丁寧に解説します。
イベントハンドラとは?
イベントハンドラは、ユーザーの操作(イベント)に対して実行される関数のことです。
よくあるイベントの例
- クリック: ボタンを押す
- 入力: テキストフィールドに文字を入力
- フォーム送信: 送信ボタンを押す
- マウス操作: ホバー、ドラッグなど
- キーボード操作: キーを押す
これらの操作に反応して、何かを実行するのがイベントハンドラの役割です。
HTMLとReactの違い
HTML:
<button onclick="handleClick()">クリック</button>React:
<button onClick={handleClick}>クリック</button>主な違い:
- イベント名がキャメルケース(
onclick→onClick) - 関数を文字列ではなく直接指定
{}で囲む
onClick – クリックイベントの基本
最もよく使うイベントハンドラから見ていきましょう。
基本的な書き方
import { useState } from 'react';
function App() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
return (
<div>
<p>クリック回数: {count}</p>
<button onClick={handleClick}>クリック</button>
</div>
);
}
export default App;ポイント
- 関数を定義:
handleClickという関数を作る - onClickに渡す:
onClick={handleClick}で関数を指定 - 括弧をつけない:
onClick={handleClick()}は❌
なぜ括弧をつけないのか?
// ❌ これは間違い
<button onClick={handleClick()}>クリック</button>括弧をつけると、レンダリング時に即座に関数が実行されてしまいます。
// ✅ 正しい書き方
<button onClick={handleClick}>クリック</button>括弧なしで渡すと、クリック時に実行される関数として登録されます。
インライン記法
簡単な処理なら、直接アロー関数を書くこともできます:
<button onClick={() => setCount(count + 1)}>クリック</button>使い分け:
- 単純な処理: インラインでOK
- 複雑な処理: 別途関数を定義
onChange – 入力イベントの処理
入力フォームで最もよく使うイベントハンドラです。
基本的な書き方
import { useState } from 'react';
function App() {
const [text, setText] = useState('');
const handleChange = (e) => {
setText(e.target.value);
};
return (
<div>
<input
type="text"
value={text}
onChange={handleChange}
placeholder="テキストを入力"
/>
<p>入力内容: {text}</p>
</div>
);
}
export default App;イベントオブジェクト(e)
handleChangeの引数eはイベントオブジェクトです。
const handleChange = (e) => {
console.log(e); // イベントオブジェクト全体
console.log(e.target); // イベントが発生した要素(input)
console.log(e.target.value); // inputの値
};重要: e.target.value
// ❌ 間違い: e.value (undefinedになる!)
const handleChange = (e) => {
setText(e.value);
};
// ✅ 正解: e.target.value
const handleChange = (e) => {
setText(e.target.value);
};私も最初はこのミスで詰まりました。必ずe.target.valueです!
インライン記法
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
/>onChangeの場合、インライン記法がよく使われます。
引数を渡したい場合
アロー関数でラップする
import { useState } from 'react';
function App() {
const [message, setMessage] = useState('');
const showMessage = (text) => {
setMessage(text);
};
return (
<div>
<button onClick={() => showMessage('おはよう')}>朝の挨拶</button>
<button onClick={() => showMessage('こんにちは')}>昼の挨拶</button>
<button onClick={() => showMessage('こんばんは')}>夜の挨拶</button>
<p>{message}</p>
</div>
);
}
export default App;なぜアロー関数が必要?
// ❌ これは即座に実行されてしまう
<button onClick={showMessage('おはよう')}>朝の挨拶</button>
// ✅ アロー関数でラップすることで、クリック時に実行される
<button onClick={() => showMessage('おはよう')}>朝の挨拶</button>複数の入力フォームを管理
実際のアプリでは、複数の入力欄を扱うことが多いです。
個別にstateを管理
import { useState } from 'react';
function App() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
return (
<div>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="名前"
/>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="メールアドレス"
/>
<p>名前: {name}</p>
<p>メール: {email}</p>
</div>
);
}
export default App;オブジェクトでまとめて管理
import { useState } from 'react';
function App() {
const [form, setForm] = useState({
name: '',
email: ''
});
const handleChange = (e) => {
const { name, value } = e.target;
setForm({
...form,
[name]: value
});
};
return (
<div>
<input
type="text"
name="name"
value={form.name}
onChange={handleChange}
placeholder="名前"
/>
<input
type="email"
name="email"
value={form.email}
onChange={handleChange}
placeholder="メールアドレス"
/>
<p>名前: {form.name}</p>
<p>メール: {form.email}</p>
</div>
);
}
export default App;ポイント
name属性を設定[name]: valueで動的にプロパティを更新- スプレッド構文(
...form)で既存の値を保持
onSubmit – フォーム送信
フォーム全体を送信する場合はonSubmitを使います。
基本的な書き方
import { useState } from 'react';
function App() {
const [name, setName] = useState('');
const [submitted, setSubmitted] = useState(false);
const handleSubmit = (e) => {
e.preventDefault(); <em>// ページのリロードを防ぐ</em>
setSubmitted(true);
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="名前を入力"
/>
<button type="submit">送信</button>
</form>
{submitted && <p>{name}さん、送信しました!</p>}
</div>
);
}
export default App;e.preventDefault() の重要性
const handleSubmit = (e) => {
e.preventDefault(); // これを忘れるとページがリロードされる!
// 処理...
};e.preventDefault()を書かないと、フォーム送信時にページ全体がリロードされてしまいます。
formとbuttonの違い
// ❌ buttonのonClickだとEnterキーが効かない
<button onClick={handleSubmit}>送信</button>
// ✅ formのonSubmitならEnterキーでも送信できる
<form onSubmit={handleSubmit}>
<button type="submit">送信</button>
</form>その他のよく使うイベントハンドラ
onFocus / onBlur – フォーカスイベント
const [isFocused, setIsFocused] = useState(false);
<input
type="text"
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
style={{ borderColor: isFocused ? 'blue' : 'gray' }}
/>onMouseEnter / onMouseLeave – マウスホバー
const [isHovered, setIsHovered] = useState(false);
<div
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
style={{ backgroundColor: isHovered ? 'lightblue' : 'white' }}
>
ホバーしてみて
</div>onKeyDown – キーボード入力
const handleKeyDown = (e) => {
if (e.key === 'Enter') {
console.log('Enterキーが押されました');
}
};
<input type="text" onKeyDown={handleKeyDown} />TODOアプリでの実例
実際のTODOアプリでは、複数のイベントハンドラを組み合わせます:
import { useState } from 'react';
function App() {
const [inputTask, setInputTask] = useState('');
const [tasks, setTasks] = useState([]);
const handleInputChange = (e) => {
setInputTask(e.target.value);
};
const handleAddTask = () => {
if (inputTask.trim() === '') return;
setTasks([...tasks, inputTask]);
setInputTask('');
};
const handleDeleteTask = (index) => {
setTasks(tasks.filter((_, i) => i !== index));
};
return (
<div>
<h1>TODOアプリ</h1>
<div>
<input
type="text"
value={inputTask}
onChange={handleInputChange}
placeholder="タスクを入力"
/>
<button onClick={handleAddTask}>追加</button>
</div>
<ul>
{tasks.map((task, index) => (
<li key={index}>
{task}
<button onClick={() => handleDeleteTask(index)}>削除</button>
</li>
))}
</ul>
</div>
);
}
export default App;このコードでは:
onChange: 入力の監視onClick: 追加ボタンonClick: 削除ボタン(引数付き)
3つのイベントハンドラを使い分けています。
まとめ
Reactのイベントハンドラについて解説しました。
重要ポイント:
- ✅ イベント名はキャメルケース(
onClick,onChange) - ✅ 関数を渡す時は括弧をつけない
- ✅
e.target.valueで入力値を取得 - ✅ フォーム送信では
e.preventDefault()を忘れずに - ✅ 引数を渡す時はアロー関数でラップ
よく使うイベントハンドラ:
onClick: クリックonChange: 入力変更onSubmit: フォーム送信onFocus/onBlur: フォーカスonMouseEnter/onMouseLeave: ホバー
イベントハンドラを理解することで、ユーザーの操作に反応するインタラクティブなアプリが作れるようになります。次回は、リストを表示するためのmap関数について詳しく解説します!
この記事のシリーズ:
- 【React入門】未経験からReactを学んで最初のTODOアプリを作った話
- 【2025年版】Viteで始めるReact開発環境の構築方法
- 【React基礎】useStateとは?初心者向けに分かりやすく解説
- 【React】イベントハンドラの書き方完全ガイド ← 今ここ
- 【React】map関数でリストをレンダリングする方法(次回)
練習問題
理解を深めるために、以下を実装してみましょう:
- カウントダウンタイマー: ボタンで開始/停止できるタイマー
- 文字数カウンター: 入力した文字数をリアルタイム表示(上限チェック付き)
- 簡易計算機: 2つの数値を入力して四則演算
次回の記事でリスト表示を学んだら、完全なTODOアプリが作れるようになります!
あとがき
月100時間の学習時間の中で、今月はReactに集中して取り組んでいます。Instagramでは、Instagramでは、フリーランスとして月収100万円を目指す道のりを、リアルタイムで発信していきますので、ぜひフォローしてください!
Instagram: @hooded_yuya_100man
