React useFormState and useFormStatus
Advertisement
React useFormState and useFormStatus
These hooks streamline form handling and submission states without complex state management.
useFormStatus
Track form submission state:
'use client'
import { useFormStatus } from 'react-dom'
function SubmitButton() {
const { pending } = useFormStatus()
return (
<button disabled={pending} type="submit">
{pending ? 'Saving...' : 'Save'}
</button>
)
}
export function ContactForm() {
return (
<form action={submitForm}>
<input name="name" required />
<textarea name="message" required />
<SubmitButton />
</form>
)
}
useFormState
Manage form submissions:
'use client'
import { useFormState } from 'react-dom'
import { submitForm } from '@/app/actions'
export function MyForm() {
const [state, formAction] = useFormState(submitForm, null)
return (
<form action={formAction}>
<input name="email" type="email" required />
<input name="message" required />
{state?.error && <p className="text-red-600">{state.error}</p>}
{state?.success && <p className="text-green-600">Sent!</p>}
<button type="submit">Send</button>
</form>
)
}
Server action:
// app/actions.ts
'use server'
export async function submitForm(previousState, formData) {
try {
const email = formData.get('email')
const message = formData.get('message')
// Validation
if (!email || !message) {
return { error: 'All fields required' }
}
// Process
await sendEmail(email, message)
return { success: true }
} catch (error) {
return { error: 'Failed to send' }
}
}
Real-World: Comment Form
'use client'
import { useFormState, useFormStatus } from 'react-dom'
import { addComment } from '@/app/actions'
function CommentSubmit() {
const { pending } = useFormStatus()
return (
<button disabled={pending} type="submit">
{pending ? 'Posting...' : 'Post Comment'}
</button>
)
}
export function CommentForm({ postId }) {
const [state, formAction] = useFormState(
(prev, data) => addComment(postId, data),
null
)
return (
<form action={formAction} className="space-y-4">
<textarea
name="content"
placeholder="Your comment..."
required
className="w-full border p-2"
/>
{state?.error && (
<p className="text-red-600">{state.error}</p>
)}
{state?.success && (
<p className="text-green-600">Comment posted!</p>
)}
<CommentSubmit />
</form>
)
}
FAQ
Q: Can I use these hooks outside of forms? A: useFormStatus must be in a descendant of form. useFormState works anywhere.
Q: How do I handle validation errors per field? A: Return field errors in state object and display conditionally.
Q: Do these hooks work with client-side validation? A: Yes, validate before calling the server action.
These hooks make form handling simple and reliable.
Advertisement