11 min read
ā¢Question 32 of 47hardAdvanced Server Actions Patterns
Complex patterns for Server Actions.
Advanced Server Actions
Optimistic Updates
code.txtTSX
'use client';
import { useOptimistic } from 'react';
import { addTodo } from './actions';
export function TodoList({ todos }: { todos: Todo[] }) {
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
todos,
(state, newTodo: Todo) => [...state, newTodo]
);
async function handleSubmit(formData: FormData) {
const newTodo = {
id: Date.now(),
text: formData.get('text'),
pending: true,
};
addOptimisticTodo(newTodo);
await addTodo(formData);
}
return (
<form action={handleSubmit}>
<input name="text" />
<button type="submit">Add</button>
<ul>
{optimisticTodos.map((todo) => (
<li key={todo.id} style={{ opacity: todo.pending ? 0.5 : 1 }}>
{todo.text}
</li>
))}
</ul>
</form>
);
}Error Handling with useFormState
code.txtTSX
'use client';
import { useFormState, useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending } = useFormStatus();
return <button disabled={pending}>{pending ? 'Saving...' : 'Save'}</button>;
}
export function Form() {
const [state, action] = useFormState(updateUser, { error: null });
return (
<form action={action}>
<input name="email" />
{state.error && <p className="error">{state.error}</p>}
<SubmitButton />
</form>
);
}Progressive Enhancement
code.txtTSX
// Works without JavaScript
'use server';
export async function subscribe(formData: FormData) {
const email = formData.get('email');
await db.subscribers.create({ email });
redirect('/thank-you');
}
// Form works with or without JS
<form action={subscribe}>
<input name="email" type="email" required />
<button>Subscribe</button>
</form>