next/form
Form
コンポーネントがNext.jsのバージョン15で追加されました。
Form
はform
を拡張したコンポーネントで、画面遷移する検索フォームやサーバーアクションによるミューテーションで利用されます。
import Form from 'next/form'
import { createPost } from '@/posts/actions'
------------------------------------------------
// 検索フォーム
function Page() {
return (
<Form action="/search">
<input name="query" />
<button type="submit">Submit</button>
</Form>
)
}
------------------------------------------------
// サーバーアクション
function Page() {
return (
<Form action={createPost}>
<input name="title" />
<button type="submit">Create Post</button>
</Form>
)
}
action
には文字列と関数(サーバーアクション)を渡せます。
文字列が渡されたときは送信のタイミングでナビゲーションが行われます。遷移先はaction
に渡した文字列をパスに、フォームデータを元にした検索パラメータを組み合わせたURLになります。上記の例では/search?query=xxx
に遷移します(xxx
はinputの入力)。
ナビゲーションはLink
コンポーネントと同じような振る舞いをします。JavaScriptが読み込まれていないときはハードナビゲーションが、読み込まれている場合はソフトナビゲーションが行われます。
さらに、フォームが表示されたタイミングで遷移先のレイアウト(layout.tsx
)と読み込み状態(loading.tsx
)を事前に取得します。
これらの機能を持つ`Form`コンポーネントを実装した例
// Note: This is abbreviated for demonstration purposes.
// Not recommended for use in production code.
'use client'
import { useEffect } from 'react'
import { useRouter } from 'next/navigation'
export default function Form(props) {
const action = props.action
const router = useRouter()
useEffect(() => {
// if form target is a URL, prefetch it
if (typeof action === 'string') {
router.prefetch(action)
}
}, [action, router])
function onSubmit(event) {
event.preventDefault()
// grab all of the form fields and trigger a `router.push` with the data URL encoded
const formData = new FormData(event.currentTarget)
const data = new URLSearchParams()
for (const [name, value] of formData) {
data.append(name, value as string)
}
router.push(`${action}?${data.toString()}`)
}
if (typeof action === 'string') {
return <form onSubmit={onSubmit} {...props} />
}
return <form {...props} />
}
遷移前に処理を行う
Form
を用いた遷移の前に、フォームデータのバリデーションのような何らかの処理を挟みたい場合は、Form
にonSubmit
を渡します。
function Page() {
const handleSubmit = (e) => {
validateFormData(e.currentTarget);
};
return (
<Form action="/search" onSubmit={handleSubmit}>
<input name="query" />
<button type="submit">Submit</button>
</Form>
)
}
上記の例では遷移の前にvalidateFormData
が実行されます。
これだけではvalidateFormData
の結果によらず、実行後に規定の遷移を誘発してしまいます。
onSubmit
の処理によって、遷移を停止したい場合は、e.preventDefault()
を呼び出します(参考)
function Page() {
const handleSubmit = (e) => {
const result = validateFormData(e.currentTarget);
if (!result.success) {
e.preventDefault();
}
};
return (
<Form action="/search" onSubmit={handleSubmit}>
<input name="query" />
<button type="submit">Submit</button>
</Form>
)
}
この例ではvalidateFormData
が失敗した場合はe.preventDefault()
呼ばれるので、遷移が行われません。
特定の値を返したり、早期returnすることで遷移されない期待も抱いてしまうのは否めないので、e.preventDefault()
を忘れないようにしましょう。