はじめに
React学習中に、preventDefault() とは何たるやとなったので記します。
MDNには以下のようにあります。
preventDefault() は Event インターフェイスのメソッドで、ユーザーエージェントに、このイベントが明示的に処理されない場合に、その既定のアクションを通常どおりに行うべきではないことを伝えます。
正直、読んだだけでは「へー、そんなものがあるのか」くらいの感想でした。
この記事では、これをかみ砕いて整理します。
preventDefault()をわかりやすく
一言でいうと、
ブラウザが標準で勝手にやる動作(既定のアクション)を止めるメソッドです。
ここでいう「ユーザーエージェント」はブラウザのこと、「既定のアクション」はブラウザが元から持っているお決まりの動作のことです。
たとえば次のようなものがあります。
| 要素・イベント | ブラウザの既定動作 |
|---|---|
<form> の送信(submit) |
action のURLへ送信し、**ページを再読み込み(遷移)**する |
<a href="..."> のクリック |
リンク先へページ遷移する |
| チェックボックスのクリック | チェックのON/OFFが切り替わる |
| 右クリック(contextmenu) | ブラウザの右クリックメニューを表示する |
| キー入力(keydown) | 入力欄に文字が入る/ショートカットが動く |
preventDefault() は、これらの「いつもの動作」をキャンセルします。
ないとどうなるのか
Reactで一番よく出会うのがフォーム送信です。
import { useState } from "react";
function TodoForm() {
const [text, setText] = useState("");
const [todos, setTodos] = useState([]);
const handleSubmit = (e) => {
// e.preventDefault(); ← これがない場合
setTodos([...todos, text]);
setText("");
};
return (
<>
<form onSubmit={handleSubmit}>
<input value={text} onChange={(e) => setText(e.target.value)} />
<button type="submit">追加</button>
</form>
<ul>
{todos.map((todo, index) => (
<li key={index}>{todo}</li>
))}
</ul>
</>
);
}
export default TodoForm;
preventDefault() がないと、送信ボタンを押した瞬間に次のことが起きます。
-
handleSubmitが実行される - その後、ブラウザが既定動作としてフォームを送信し、ページを再読み込みする
- Reactの state がすべてリセットされる(追加したTodoが一瞬で消える)
「追加したはずなのに一瞬で消える」「画面がチラッとする」「URLの末尾に ? が付く」となったら、だいたいこれが原因です。
実際に確認してみましょう。
追加したTodoが一瞬で消えてしまいます。
💡 補足
Reactは、ページを再読み込みせずに画面を書き換える仕組みです。
stateはブラウザのメモリ上で管理されているため、ページを再読み込みすると初期値に戻ってしまいます。
付けた場合の効果
先ほどコメントアウトした e.preventDefault(); のコメントを外します。
const handleSubmit = (e) => {
e.preventDefault(); // ブラウザの「送信&再読み込み」を止める
setTodos([...todos, text]);
setText("");
};
これで、
- ページの再読み込みが起きない
- state が保持される
- 送信時の処理をすべて自分(JavaScript側)でコントロールできる
ようになります。
「ブラウザに任せず、ここから先は自分でやります」という宣言だと考えるとわかりやすいです。
実際に確認してみましょう。
無事に追加されていますね。
💡 補足
handleSubmitの中でconsole.log(e)を書き、開発者ツールのコンソールで確認すると、次のように表示されます。いろいろな項目が並んでいますが、今回は
defaultPrevented: trueに注目します。
これがtrueになっていれば、preventDefault()が効いて、ブラウザの既定動作(ページの再読み込み)が止まっているということです。注意点として、
defaultPreventedがtrueになるのは、cancelable: true(止められるイベント)の場合だけです。cancelableがfalseのイベントでは、preventDefault()を呼んでもdefaultPreventedはfalseのままで、既定動作も止まりません。また、すぐ下の
isPropagationStopped()はfalseを返しています。preventDefault()で止まるのは既定動作だけで、親要素へのイベントの伝わり(伝播)は止まっていないことがわかります。
範囲(何を止めて、何を止めないのか)
ここが混乱しやすいポイントです。preventDefault() が止めるのは既定動作だけです。
| 止めるもの・止めないもの | preventDefault() |
|---|---|
| ブラウザの既定動作(遷移・送信・チェック切替など) | 止める |
| イベントの伝播(親要素への伝わり=バブリング) | 止めない |
| 同じ要素に登録された他のイベントハンドラ | 止めない |
| 自分のハンドラ内の残りの処理 | 止めない(return ではない) |
伝播を止めたい場合は、別のメソッドを使います。
| メソッド | 役割 |
|---|---|
preventDefault() |
ブラウザの既定動作を止める |
stopPropagation() |
親要素などへのイベントの伝播を止める |
stopImmediatePropagation() |
伝播を止め、同じ要素に登録された他のリスナーも止める(ネイティブイベント) |
たとえば、onClick を持つ親要素の中にあるリンクをクリックした場合、組み合わせごとの結果は次のとおりです。
| 書いたもの | リンク遷移 | 親の onClick |
|---|---|---|
| どちらも書かない | する | 動く |
preventDefault() だけ |
しない | 動く |
stopPropagation() だけ |
する | 動かない |
| 両方 | しない | 動かない |
stopPropagation() だけではリンク遷移は止まらない、という点に注意です。
注意点:await より前に呼ぶ
async 関数のハンドラでは、最初の await で処理が一時停止した時点で、ブラウザは既定動作を実行してしまいます。
そのため、await の後で preventDefault() を呼んでも間に合いません。
// ❌ await の間にブラウザが送信してしまう
const handleSubmit = async (e) => {
await saveData();
e.preventDefault();
};
// ✅ 最初に呼ぶ
const handleSubmit = async (e) => {
e.preventDefault();
await saveData();
};
「ハンドラの先頭で呼ぶ」と覚えておけば安全です。
まとめ
preventDefault() は、ブラウザが標準で行う動作(既定動作)を止めるメソッドです。
Reactは、ページを再読み込みせずに、JavaScriptで画面を書き換えて動きます。
一方でブラウザは、昔ながらの「送信したらページを読み直す」「クリックしたら別のページへ移る」といった動作を、標準で勝手に行います。
この2つがぶつかる場面で、preventDefault() が必要になります。
ないと困る場面
| 場面 | ないとどうなるか |
|---|---|
| フォームの送信 | ページが再読み込みされ、state や入力内容が消える。送信中のAPI通信が途中で切れることもある |
| 入力欄でEnterキーを押す | フォーム内の入力欄でEnterを押すだけで送信扱いになり、同じく再読み込みされる |
リンク(<a>)をボタン代わりに使う |
クリックしたときの処理より先にページが移動してしまう。 |
| 入力チェック(バリデーション)をしたいとき | エラーを表示する前にページが再読み込みされ、エラーメッセージも入力内容も消える |
どれも、ブラウザの「いつもの動作」が、自分で書いた処理の邪魔をしているケースです。
押さえておきたいポイント
- 効いているかは、イベントの
defaultPreventedがtrueかどうかで確認できる - 止めるのは既定動作だけ。親要素への伝播を止めるのは
stopPropagation()の役割 -
asyncのハンドラでは、awaitより前に呼ぶ
おわりに
最初はMDNの説明を読んでもピンときませんでしたが、実際に外して動かしてみると、「Todoが一瞬で消える」という形で違いがはっきり目に見えました。
「ブラウザが勝手に何かしてしまう」と感じたら、まず preventDefault() を疑ってみる。
そして、ブラウザに任せたほうがいい動作(普通のリンク遷移など)は止めずに、止める理由がある場面だけで使う。
この2つを意識しておけば、迷わず使えそうです。


