第139章:useFormStatus の雰囲気(子が送信中を知る)🧩✨
この章では、フォームの「送信中…😵💫」を“子コンポーネント側”で自然に知るための useFormStatus を触ります🫶
<SubmitButton /> みたいな部品が、親フォームの状態を props なしで勝手に察してくれるのが最高ポイントです💡 (React)
1) 今日のゴール🎯💖
- 送信ボタンを 送信中は disabled にする🛑
- ボタンの文言を 「送信中…」 に変える⏳
- それを フォームの子コンポーネント(SubmitButton)だけで実現する🧸✨ (React)
2) useFormStatus ってなに?🤔🧠
useFormStatus は、直近のフォーム送信の状態を教えてくれるフックです📨✨
特に使うのはこのへん👇
pending:送信中かどうか(これが一番使う!)⏳data/method/action:送信データや送信方法など(必要になったらでOK)🧩 (React)
✅大事ポイント:
useFormStatus は <form> の中でレンダーされるコンポーネントでしか使えません🙅♀️
(だから SubmitButton を “フォームの中の部品” にするのが定番です) (React)
3) 図でつかむ:どこで動くの?🗺️✨
「フォームの中にいる子」だけが、フォームの送信状態を受け取れるイメージです👶💡 (React)
4) 実装してみよう!📮✨(最小サンプル)
フォルダ構成(今回使うところだけ)🗂️
app/contact/page.tsxcomponents/SubmitButton.tsx
(1) components/SubmitButton.tsx(送信中を知る子ボタン)🔘⏳
'use client'
import { useFormStatus } from 'react-dom'
export function SubmitButton() {
const { pending } = useFormStatus()
return (
<button
type="submit"
disabled={pending}
aria-disabled={pending}
style={{
padding: '10px 14px',
borderRadius: 10,
border: '1px solid #ddd',
background: pending ? '#f3f3f3' : 'white',
cursor: pending ? 'not-allowed' : 'pointer',
}}
>
{pending ? '送信中…⏳' : '送信する📮'}
</button>
)
}
useFormStatusはreact-domから import します✅ (React)pendingがtrueの間は 押せない&表示が変わる✨
(2) app/contact/page.tsx(Server Action付きフォーム)🧊🧾
import { SubmitButton } from '@/components/SubmitButton'
export default function ContactPage() {
async function sendContact(formData: FormData) {
'use server'
const email = String(formData.get('email') ?? '')
const message = String(formData.get('message') ?? '')
// 本当はDB保存やメール送信などをする想定📦✉️
console.log({ email, message })
// わざと少し待って「送信中」を見えやすくする🫧
await new Promise((r) => setTimeout(r, 1200))
}
return (
<main style={{ maxWidth: 520, margin: '40px auto', padding: 16 }}>
<h1 style={{ fontSize: 24, marginBottom: 12 }}>お問い合わせ📮</h1>
<form action={sendContact} style={{ display: 'grid', gap: 12 }}>
<label style={{ display: 'grid', gap: 6 }}>
<span>メール✉️</span>
<input
name="email"
type="email"
required
placeholder="aki@example.com"
style={{ padding: 10, borderRadius: 10, border: '1px solid #ddd' }}
/>
</label>
<label style={{ display: 'grid', gap: 6 }}>
<span>内容📝</span>
<textarea
name="message"
required
rows={5}
placeholder="こんにちは!相談があります…"
style={{ padding: 10, borderRadius: 10, border: '1px solid #ddd' }}
/>
</label>
<SubmitButton />
</form>
</main>
)
}
ポイントはここ👇
<form action={sendContact}>で Server Action に送る🧾✨ (Next.js)<SubmitButton />が フォームの中にいるからpendingを受け取れる🎁 (React)
5) 送信の流れ(ざっくり)📨➡️🧠
6) よくあるハマり🔥(ここだけ覚えればOK!)
-
useFormStatusをフォームの外で使ってる →pendingが反映されない😭 👉「<form>の子」で使う、が鉄則です🧸 (React) -
"use client"を忘れる → フックが使えない🙅♀️ 👉SubmitButton.tsx側に付けよう🎮 (Zenn)
7) ミニ課題🎒✨(10〜15分)
-
SubmitButtonの横に、送信中だけ出るテキストを追加してみてね👇- 例:「いま送信してるよ…ちょい待ち🙏」
-
送信中は
inputとtextareaも触れないようにしてみよう🧊- ヒント:
useFormStatusを使う 別の小コンポーネントを作って、pendingでdisabledを切り替えるのがやりやすいよ😉
- ヒント:
必要なら次の章(第140章)につながる形で、useOptimistic と「先に表示しちゃう✨」の超ミニ例もセットで作るよ〜😆