Suggestions for adding instructions on how to retain previously entered valid data when form submission fails due to server-side validation errors.
One of the burning question after following Chapter 14 of adding server-side data validation is that: it would be really helpful if we do not ask user to refill the form from scratch when submission fails due to data violation, an example could be that say user had picked a customer and entered an amount but forget to pick a "state", and then click on "create invoice", instead of wiping out the whole form, and ask user to start from scratch, it would be much better if we can show (extremely helpful for beginners) how we can return the filled data back with server action function and use that to fill the form.
so something like this:
// under app\lib\actions.ts
export async function createInvoice(prevState: State, formData: FormData) {
const validatedFields = CreateInvoice.safeParse({
customerId: formData.get('customerId'),
amount: formData.get('amount'),
status: formData.get('status'),
})
if (!validatedFields.success) {
return {
errors: validatedFields.error.flatten().fieldErrors,
message: 'Missing Fields. Failed to Create Invoice.',
// Returning back previously entered data here
prevState: {
customerId: formData.get('customerId'),
amount: formData.get('amount'),
status: formData.get('status'),
},
}
}
// continue
}
Also, it seems that setting the previously picked customer correct in "select" when server action failed is very tricky, and it took very long time for me to get a version working with:
// under app\ui\invoices\create-form.tsx
const previousCustomer = customers.find(
(c) => c.id === state?.prevState?.customerId
)
// after return statement
<select
id="customer"
name="customerId"
className="peer block w-full cursor-pointer rounded-md border border-gray-200 py-2 pl-10 text-sm outline-2 placeholder:text-gray-500"
defaultValue=""
aria-describedby="customer-error"
>
<option
value={previousCustomer ? previousCustomer.id : ''}
disabled={!previousCustomer}
>
{previousCustomer ? previousCustomer.name : 'Select a customer'}
</option>
{customers
.filter((c) => c.id !== previousCustomer?.id)
.map((customer) => (
<option key={customer.id} value={customer.id}>
{customer.name}
</option>
))}
</select>
State does lack 'prevState', so your example is not working. Maybe you could be so nice and add your whole solution to this problem.
State does lack 'prevState', so your example is not working. Maybe you could be so nice and add your whole solution to this problem.
did you type State accordingly?
One of the burning question after following Chapter 14 of adding server-side data validation is that: it would be really helpful if we do not ask user to refill the form from scratch when submission fails due to data violation... it would be much better if we can show (extremely helpful for beginners) how we can return the filled data back with server action function and use that to fill the form.
Thanks for posting this excellent solution! It is extremely helpful.
Linked issue :
- https://github.com/facebook/react/issues/29034
Previously this tutorial used useFormState here. With React 19 this hook got deprecated and replaced with useActionState - they made it sound like it was simply renamed but it seems to behave differently regards resetting the form after submission.
I found the same solution here - include the form values in state and use these as the defaultValue (rather than the value from the database).