useFormState is a React hook for working with forms and server actions:
import { useFormState } from 'react-dom';
function ContactForm() {
const [state, formAction] = useFormState(submitForm, null);
return (
<form action={formAction}>
<input name="email" type="email" />
<button type="submit">Submit</button>
{state?.error && <p>{state.error}</p>}
</form>
);
}useFormState is a modern hook for working with forms in React! It simplifies integration with server actions and form state management. 🚀
import { useFormState } from 'react-dom';
const [state, formAction] = useFormState(action, initialState);// action - server function
async function submitForm(prevState, formData) {
// Process form data
const email = formData.get('email');
try {
await sendEmail(email);
return { success: true, message: 'Sent!' };
} catch (error) {
return { error: 'Send error' };
}
}
// initialState - initial state
const initialState = { message: null, error: null };
function MyForm() {
const [state, formAction] = useFormState(submitForm, initialState);
return (
<form action={formAction}>
<input name="email" required />
<button type="submit">Submit</button>
{state?.success && <p>✅ {state.message}</p>}
{state?.error && <p>❌ {state.error}</p>}
</form>
);
}async function contactAction(prevState, formData) {
const name = formData.get('name');
const email = formData.get('email');
const message = formData.get('message');
// Validation
if (!name || !email || !message) {
return { error: 'Fill all fields' };
}
try {
await sendContactForm({ name, email, message });
return { success: true, message: 'Message sent!' };
} catch (error) {
return { error: 'Server error' };
}
}
function ContactForm() {
const [state, formAction] = useFormState(contactAction, null);
return (
<form action={formAction}>
<input name="name" placeholder="Name" required />
<input name="email" type="email" placeholder="Email" required />
<textarea name="message" placeholder="Message" required />
<button type="submit">Submit</button>
{state?.success && (
<div className="success">✅ {state.message}</div>
)}
{state?.error && (
<div className="error">❌ {state.error}</div>
)}
</form>
);
}async function registerAction(prevState, formData) {
const username = formData.get('username');
const password = formData.get('password');
// Validation
if (password.length < 6) {
return { error: 'Password must be at least 6 characters' };
}
try {
const user = await createUser({ username, password });
return { success: true, user };
} catch (error) {
return { error: 'User already exists' };
}
}
function RegisterForm() {
const [state, formAction] = useFormState(registerAction, null);
return (
<form action={formAction}>
<input name="username" placeholder="Username" required />
<input name="password" type="password" placeholder="Password" required />
<button type="submit">Register</button>
{state?.success && (
<p>Welcome, {state.user.username}!</p>
)}
{state?.error && <p className="error">{state.error}</p>}
</form>
);
}async function uploadAction(prevState, formData) {
const file = formData.get('file');
if (!file || file.size === 0) {
return { error: 'Select a file' };
}
if (file.size > 5 * 1024 * 1024) {
return { error: 'File too large (max 5MB)' };
}
try {
const url = await uploadFile(file);
return { success: true, url };
} catch (error) {
return { error: 'Upload failed' };
}
}
function UploadForm() {
const [state, formAction] = useFormState(uploadAction, null);
return (
<form action={formAction}>
<input name="file" type="file" accept="image/*" required />
<button type="submit">Upload</button>
{state?.success && (
<div>
<p>✅ File uploaded!</p>
<img src={state.url} alt="Uploaded" width="200" />
</div>
)}
{state?.error && <p className="error">{state.error}</p>}
</form>
);
}import { useFormState } from 'react-dom';
import { useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Submitting...' : 'Submit'}
</button>
);
}
function MyForm() {
const [state, formAction] = useFormState(submitAction, null);
return (
<form action={formAction}>
<input name="data" required />
<SubmitButton />
{state?.error && <p>{state.error}</p>}
</form>
);
}async function validateAction(prevState, formData) {
const email = formData.get('email');
const age = formData.get('age');
const errors = {};
// Email validation
if (!email.includes('@')) {
errors.email = 'Invalid email format';
}
// Age validation
if (age < 18) {
errors.age = 'Age must be 18+';
}
if (Object.keys(errors).length > 0) {
return { errors };
}
// Save data
await saveUser({ email, age });
return { success: true };
}
function ValidationForm() {
const [state, formAction] = useFormState(validateAction, null);
return (
<form action={formAction}>
<div>
<input name="email" type="email" placeholder="Email" />
{state?.errors?.email && (
<span className="error">{state.errors.email}</span>
)}
</div>
<div>
<input name="age" type="number" placeholder="Age" />
{state?.errors?.age && (
<span className="error">{state.errors.age}</span>
)}
</div>
<button type="submit">Save</button>
{state?.success && <p>✅ Data saved!</p>}
</form>
);
}import { useOptimistic } from 'react';
async function addCommentAction(prevState, formData) {
const comment = formData.get('comment');
// Simulate server delay
await new Promise(resolve => setTimeout(resolve, 1000));
const newComment = {
id: Date.now(),
text: comment,
author: 'User'
};
return { success: true, comment: newComment };
}
function CommentForm({ comments, onAddComment }) {
const [state, formAction] = useFormState(addCommentAction, null);
const [optimisticComments, addOptimisticComment] = useOptimistic(
comments,
(state, newComment) => [...state, newComment]
);
return (
<div>
<form
action={async (formData) => {
const comment = formData.get('comment');
addOptimisticComment({
id: Date.now(),
text: comment,
author: 'User'
});
const result = await formAction(formData);
if (result?.success) {
onAddComment(result.comment);
}
}}
>
<textarea name="comment" placeholder="Your comment" />
<button type="submit">Add</button>
</form>
<div>
{optimisticComments.map(comment => (
<div key={comment.id}>{comment.text}</div>
))}
</div>
</div>
);
}// ✅ Correct
async function goodAction(prevState, formData) {
try {
// Validation
const data = validateFormData(formData);
// Processing
const result = await processData(data);
return { success: true, data: result };
} catch (error) {
return { error: error.message };
}
}❌ Wrong:
// Forgot error handling
async function badAction(prevState, formData) {
const result = await api.call(formData); // Can crash
return result;
}✅ Correct:
// Proper error handling
async function goodAction(prevState, formData) {
try {
const result = await api.call(formData);
return { success: true, data: result };
} catch (error) {
return { error: 'Something went wrong' };
}
}useFormState is a powerful tool for working with forms:
Use it to create modern and responsive forms! 🎯