React FormEvent を使ったら「非推奨」と言われて困っていませんか?または React のイベント処理(クリック・入力・フォーム)の書き方に迷っていませんか?
この記事では、React FormEvent の現状と preventDefault の正しい使い方、そして TypeScript で型を付けたイベント処理の書き方 まで、実例で解説します。
読み終わる頃には、自分のコードでイベント処理を自信を持って書き分けられるようになります。
Contents
結論:React FormEvent は今も使えるが、より良い書き方がある
短く先にお伝えすると:
React.FormEventは今も動作するものの、新しい React の型定義では TypeScript から「非推奨」の警告 が出ます。- 代わりに
React.SubmitEventを使うのが推奨されています。event.preventDefault()などの動作はそのまま使えます。 - 既存コードを書き換える場合も、型を
FormEvent→SubmitEventに置き換えるだけで済むケースがほとんどです。
// 旧(非推奨警告が出る)
const handleSubmit = (event: React.FormEvent) => {
event.preventDefault()
const formData = new FormData(event.target as HTMLFormElement)
// ...
}
// 新(推奨)
const handleSubmit = (event: React.SubmitEvent) => {
event.preventDefault()
const formData = new FormData(event.target as HTMLFormElement)
// ...
}
それでは、React のイベント処理の基本から、上記の置き換えに至るまでを順に詳しく見ていきましょう。
参照元
本記事は以下の Zenn 教材で React の基本を学んだうえで、自分なりに整理し直したものです。
イベント処理の章を中心に、TypeScript の型定義と組み合わせた書き方を実際にハンズオンしながら確認しました。
得られるもの
この記事を読むと、以下のことができるようになります。
- React で クリック・入力・フォーム のイベントを TypeScript の型付きで書ける
React.MouseEvent/React.ChangeEvent/React.SubmitEventなどのイベント型を場面に応じて使い分けられるevent.targetとevent.currentTargetの違いを理解して使い分けられるpreventDefaultを使って、フォーム送信やリンク遷移のデフォルト動作を防止できるReact.FormEventの非推奨化にどう対応するかがわかる
プロジェクトのフォルダ構成
ハンズオンで作成したファイルは以下のとおりです。
src/
├── App.css
├── App.tsx
├── assets/
├── ClickExample.tsx
├── EventExample.tsx
├── FormExample.tsx
├── InputExample.tsx
├── index.css
└── main.tsx
ClickExample.tsx / InputExample.tsx / EventExample.tsx / FormExample.tsx の 4 つのコンポーネントで、それぞれ別の種類のイベントを扱います。
前提条件
以下が完了している前提で進めます。
- Vite + React + TypeScript プロジェクトの作成
- Tailwind CSS の設定
セットアップがまだの方は、先に Vite + React + TypeScript の環境構築記事 を参照してください。
クリックイベントの基本
まずは一番馴染みのある クリックイベント から見ていきます。ClickExample.tsx を以下のように書きました。
const ClickExample = () => {
const showMessage = (message: string) => {
alert(message)
}
const showTime = () => {
const now = new Date()
alert(`現在の時刻: ${now.toLocaleTimeString()}`)
}
return (
<div className="p-8">
<h2 className="text-2xl font-bold mb-6">様々なクリックイベント</h2>
<div className="space-y-4">
<button
onClick={() => showMessage('こんにちは!')}
className="block bg-blue-500 text-white px-6 py-3 rounded-lg hover:bg-blue-600"
>
挨拶メッセージ
</button>
<button
onClick={showTime}
className="block bg-green-500 text-white px-6 py-3 rounded-lg hover:bg-green-600"
>
現在時刻を表示
</button>
</div>
</div>
)
}
export default ClickExample
ポイントは onClick の渡し方が 2 種類ある ことです。
- 引数あり:
onClick={() => showMessage('こんにちは!')}のようにアロー関数でラップする - 引数なし:
onClick={showTime}のように関数名を直接渡す
引数を渡したい場合に onClick={showMessage('こんにちは!')} と書いてしまうと、レンダリング時に即座に実行されてしまうため注意してください。
ブラウザで表示すると、以下のように 2 つのボタンが表示されます。

「挨拶メッセージ」ボタンをクリックすると、以下のように alert が表示されます。

「現在時刻を表示」ボタンをクリックすると、現在時刻が alert に表示されます。

クリックイベントの基本はこれだけです。次は入力フォームを扱います。
入力イベントの処理
次に 入力イベント(onChange)を見ていきます。InputExample.tsx を以下のように書きました。
const InputExample = () => {
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
console.log('入力された値:', event.target.value)
}
const handleSubmit = () => {
const inputElement = document.getElementById('nameInput') as HTMLInputElement
const inputValue = inputElement.value
if (inputValue.trim() === '') {
alert('名前を入力してください')
} else {
alert(`こんにちは、${inputValue}さん!`)
}
}
return (
<div className="p-8">
<h2 className="text-2xl font-bold mb-6">入力イベントの例</h2>
<div className="max-w-md">
<label className="block text-sm font-medium mb-2">
お名前を入力してください
</label>
<input
id="nameInput"
type="text"
onChange={handleInputChange}
placeholder="名前を入力"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:border-blue-500"
/>
<button
onClick={handleSubmit}
className="mt-4 bg-blue-500 text-white px-6 py-2 rounded-lg hover:bg-blue-600"
>
送信
</button>
</div>
</div>
)
}
export default InputExample
ブラウザで表示すると、入力フォームと送信ボタンが表示されます。

何も入力せず送信ボタンを押すと、バリデーションが働きます。

名前を入力して送信すると、挨拶メッセージが表示されます。

onChange イベントは入力のたびに発火するため、コンソールには入力中の値が逐次出力されます。

イベントの型について
入力イベントのハンドラには、TypeScript で以下の型を指定しています。
(event: React.ChangeEvent<HTMLInputElement>) => { ... }
React.ChangeEvent<HTMLInputElement> は、「<input> 要素で発生した change イベント」 を表す型です。event.target.value のような書き方が型エラーにならないのは、この型指定によって target が HTMLInputElement だと TypeScript が認識できているためです。
C# 経験者向けに補足すると、EventHandler<TEventArgs> の TEventArgs を指定しているのと同じイメージです。React 側ではジェネリクスで対象の HTML 要素を渡す形になっています。
document.getElementById は React 的にアンチパターン
このサンプルでは handleSubmit 内で document.getElementById('nameInput') を使って入力値を取得していますが、これは React では推奨されない書き方 です。
Reactでは、DOM直接操作を避けて状態(state)で管理するのが基本的な設計思想です。
正しくは useState で入力値を管理し、ボタンクリック時にその state を読むべきです。今回は「onChange がどう動くか」を見るために素朴に書いていますが、実際のアプリでは useState を使ったフォーム管理 に置き換えることを意識してください。
イベントオブジェクトの型定義
React のイベント処理では、React.○○Event<HTML要素> というパターンの型を場面に応じて使い分けます。EventExample.tsx で複数のイベント型をまとめて確認しました。
const EventExample = () => {
const handleMouseEvent = (event: React.MouseEvent<HTMLDivElement>) => {
console.log('マウスの位置:', event.clientX, event.clientY)
console.log('クリックされた要素:', event.currentTarget.tagName)
}
const handleKeyPress = (event: React.KeyboardEvent<HTMLInputElement>) => {
console.log('押されたキー:', event.key)
if (event.key === 'Enter') {
alert('Enterキーが押されました!')
}
}
const handleFocus = (event: React.FocusEvent<HTMLInputElement>) => {
event.target.style.backgroundColor = 'lightyellow'
}
const handleBlur = (event: React.FocusEvent<HTMLInputElement>) => {
event.target.style.backgroundColor = 'white'
}
return (
<div className="p-8">
<h2 className="text-2xl font-bold mb-6">イベントオブジェクトの例</h2>
<div className="space-y-4">
<div>
<button
onClick={handleMouseEvent}
className="bg-green-500 text-white px-6 py-3 rounded-lg hover:bg-green-600"
>
クリック位置を表示(コンソールを確認)
</button>
</div>
<div>
<input
type="text"
onKeyDown={handleKeyPress}
placeholder="何か入力してください(Enterキーを試してみて)"
className="w-full max-w-md px-3 py-2 border border-gray-300 rounded-lg"
/>
</div>
<div>
<input
type="text"
onFocus={handleFocus}
onBlur={handleBlur}
placeholder="フォーカス時に背景色が変わります"
className="w-full max-w-md px-3 py-2 border border-gray-300 rounded-lg"
/>
</div>
</div>
</div>
)
}
export default EventExample
ブラウザで表示すると、ボタンと 2 つの input が並びます。

ボタンをクリックすると、コンソールにマウスの座標と要素名が出力されます。

input に文字を入力して Enter を押すと、alert が出ます。

下の input にフォーカスすると背景色が変わります。

イベント型の一覧
ここまでに登場したイベントを表でまとめると、以下のようになります。
| イベントハンドラ | 型 | 主な用途 |
|---|---|---|
| onChange | React.ChangeEvent | input / select / textarea の値変更 |
| onClick / onMouseMove | React.MouseEvent | クリック・マウス操作 |
| onKeyDown / onKeyPress | React.KeyboardEvent | キーボード操作 |
| onFocus / onBlur | React.FocusEvent | フォーカス・ブラー |
| onSubmit | React.SubmitEvent | フォーム送信(旧 React.FormEvent) |
ジェネリクスで対象の HTML 要素を指定すると、event.target や event.currentTarget の型が確定して補完が効くようになります。
target と currentTarget の違い
event オブジェクトには target と currentTarget の 2 つのプロパティがあり、混同しやすい部分です。
event.target: 実際にイベントが発生した要素(子要素の可能性あり)event.currentTarget: イベントハンドラが設定された要素自体
たとえば <button> の中に <span> があり、<span> をクリックした場合、
event.target→<span>event.currentTarget→<button>
となります。多くの場合は currentTarget を使うほうが安全です。
target を DOM 要素として扱いたい場合は、型アサーションが必要になります。
(event.target as HTMLElement).tagName
FormEvent の非推奨について
最後に、最近よく検索されている React.FormEvent の話題です。
冒頭の結論でもお伝えした通り、React.FormEvent は今も動作しますが、新しい React の型定義(@types/react)では非推奨マークが付いています。
代わりに React.SubmitEvent を使うのが推奨されています。実際のサンプルコードは次のセクションで確認します。
デフォルト動作の防止(preventDefault)
最後に、フォーム送信やリンククリックの デフォルト動作を防止する preventDefault を扱います。FormExample.tsx は以下のとおりです。
const FormExample = () => {
const handleSubmit = (event: React.SubmitEvent) => {
event.preventDefault()
const formData = new FormData(event.target as HTMLFormElement)
const name = formData.get('name')
const email = formData.get('email')
alert(`フォーム送信処理\n名前: ${name}\nメール: ${email}`)
}
const handleLinkClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
event.preventDefault()
alert('リンクのデフォルト動作を防ぎました')
}
return (
<div className="p-8">
<h2 className="text-2xl font-bold mb-6">preventDefault()の例</h2>
<div className="space-y-8">
<div>
<h3 className="text-lg font-semibold mb-4">フォーム送信の例</h3>
<form onSubmit={handleSubmit} className="max-w-md space-y-4">
<div>
<label className="block text-sm font-medium mb-1">名前</label>
<input
name="name"
type="text"
required
className="w-full px-3 py-2 border border-gray-300 rounded-lg"
/>
</div>
<div>
<label className="block text-sm font-medium mb-1">メールアドレス</label>
<input
name="email"
type="email"
required
className="w-full px-3 py-2 border border-gray-300 rounded-lg"
/>
</div>
<button
type="submit"
className="w-full bg-blue-500 text-white py-2 rounded-lg hover:bg-blue-600"
>
送信(ページはリロードされません)
</button>
</form>
</div>
<div>
<h3 className="text-lg font-semibold mb-4">リンクの例</h3>
<a
href="https://example.com"
onClick={handleLinkClick}
className="text-blue-500 underline hover:text-blue-700"
>
このリンクをクリックしても移動しません
</a>
</div>
</div>
</div>
)
}
export default FormExample
ポイントは以下の 3 点です。
<form onSubmit={handleSubmit}>のハンドラ型はReact.SubmitEventを使う(React.FormEventではない)event.preventDefault()でフォームの デフォルトの送信動作(ページリロード) を防止する<a>タグのクリックでもevent.preventDefault()で リンク遷移を防止 できる
ブラウザで表示すると、フォームとリンクが並びます。

名前とメールアドレスを入力して送信ボタンを押すと、ページリロードされずに alert が表示されます。

リンクをクリックしても外部サイトに飛ばずに alert が表示されます。

preventDefault は SPA でページ遷移を制御する場面でも頻出するので、ここでしっかり押さえておきましょう。
まとめ
React のイベント処理は、TypeScript の型を組み合わせて使うことで安全に書けます。
- クリック・入力・フォーム のイベントは
React.MouseEvent/React.ChangeEvent/React.SubmitEventを使い分ける event.targetとevent.currentTargetの違いを理解しておくReact.FormEventは非推奨、代わりにReact.SubmitEventを使うpreventDefaultでフォーム送信やリンク遷移のデフォルト動作を防止できる
C# の EventHandler<TEventArgs> と同様、React.○○Event<HTML要素> というパターンで対応できると押さえれば、新しいイベント型に出会っても怖くありません。
これまでの学習内容
React 入門シリーズの関連記事です。順番に読むと体系的にキャッチアップできます。
- Vite + React + TypeScript の環境構築
- React JSX と Props の基本
- React useState と useEffect の使い方
- React useContext で Props drilling を解消する
また、React と一緒に使う TailwindCSS の逆引きガイドもあります。
それではまた、別の記事でお会いしましょう。