- SPAは「submit」を止めることで成立している
- ブラウザのネイティブsubmitはとても高機能
- SPAはsubmitをキャンセルしている
- 代わりに何が起きているのか
- なぜ問題が頻発するのか
- 特に壊れやすい「リダイレクト」
- なぜSSRでは問題が少ないのか
- よくある失敗例
- SPAでフォームを扱うときの注意点
- どう考えると楽になるか
SPAは「submit」を止めることで成立している
SPAでフォーム送信が難しくなる理由は、ReactやVueが難しいからではありません。
SPAというアーキテクチャ自体が、ブラウザ本来のフォーム送信機能を無効化して動いているからです。
つまり、SPAはフォームを「拡張」しているのではなく、いったん破壊してからJavaScriptで再実装しています。
ここを理解すると、なぜバリデーション・リダイレクト・戻るボタンなどで問題が起きるのかがはっきり見えてきます。
ブラウザのネイティブsubmitはとても高機能
まず通常のHTMLフォームの動作を整理します。
<form action="/users" method="POST"> <input name="name"> <button type="submit">送信</button> </form>
ボタンを押すと、ブラウザは次の処理を自動で行います。
- 入力値収集
- バリデーション
- リクエスト生成
- ページ遷移
- 履歴追加
JavaScriptは不要です。
さらに、Enterキー送信、フォーカス移動、オートコンプリート、アクセシビリティ支援まで含まれます。
つまりHTMLフォームは、単なるUI部品ではなくブラウザ組み込みのアプリケーション機能です。
SPAはsubmitをキャンセルしている
SPAではページ遷移を起こしてはいけません。
なぜならSPAは「単一ページ」を維持するアプリケーションだからです。
そのため、フレームワークはsubmitイベントを止めます。
form.addEventListener("submit", e => { e.preventDefault(); });
これがすべての始まりです。
この瞬間、ブラウザが持っていたフォーム機能は動作しなくなります。
代わりに何が起きているのか
SPAでは、送信処理をJavaScriptで書き直します。
async function onSubmit() { const name = input.value; await fetch("/api/users", { method: "POST", body: JSON.stringify({ name }) }); }
見た目は同じ「送信ボタン」ですが、中身はまったく別の仕組みです。
ブラウザではなくアプリケーションコードが通信を担当します。
この変更により、多くの副作用が発生します。
なぜ問題が頻発するのか
ネイティブsubmitが消えると、次の機能をすべて自分で実装する必要があります。
- 必須チェック
- エラーメッセージ
- 2重送信防止
- リダイレクト
- 履歴管理
- 戻るボタン対応
例えばEnterキー。
通常は自動送信されますが、SPAでは反応しないことがあります。
キーボードイベントを個別に処理していないためです。
また、ブラウザのバリデーションも無効になります。
<input required>
ネイティブでは未入力時に送信できません。
しかしSPAではfetchが実行され、サーバに空データが送られることがあります。
特に壊れやすい「リダイレクト」
通常のフォーム送信では、サーバは次のレスポンスを返します。
HTTP/1.1 302 Found Location: /complete
ブラウザが自動で遷移します。
しかしSPAではfetchがレスポンスを受け取るだけで、画面は変わりません。
そのため、次の処理を追加する必要があります。
router.push("/complete");
このとき履歴が正しく積まれないと、戻るボタンで再送信が起きるなどの問題が発生します。
なぜSSRでは問題が少ないのか
SSRではフォーム送信後にページ遷移が起きます。
つまりブラウザの標準挙動をそのまま使えます。
- POST
- リダイレクト
- 新ページ表示
この一連の流れは数十年ブラウザで最適化されています。
SPAはそれをJavaScriptで再現しようとしているため、複雑になります。
よくある失敗例
実務で頻繁に起きる問題です。
- 送信ボタン連打で多重登録
- 戻るで再送信警告が出ない
- オートフィルが効かない
- IME確定前に送信される
これらはフレームワークのバグではありません。
ネイティブフォームを置き換えた副作用です。
SPAでフォームを扱うときの注意点
重要なのは「フォームを普通のUIだと思わない」ことです。
フォームは単なる入力欄ではなく、
ブラウザの通信機構と履歴機構を内包した機能です。
SPAではそれを再実装していると意識すると、設計が安定します。
どう考えると楽になるか
SPAでフォームが難しいのは、フレームワークの習熟不足ではありません。
アーキテクチャ上の必然です。
SPAはページ遷移を消し、代わりにJavaScriptで状態遷移を管理します。
フォーム送信は本来「ページ遷移を伴う通信」です。
この2つは思想が衝突します。
だから多くのモダンフレームワークは最近、ネイティブsubmitへ回帰する仕組み(Server Actionsなど)を導入し始めています。
フォームで悩んだときは、
「なぜこんなに面倒なのか」ではなく
「ブラウザが本来やっていた仕事を自分が背負っている」と考えると、設計の方向が見えやすくなります。