Preserving Form State
Ausath Ikram • Oct 19, 2024
On my last post, we learned how to validate forms using useActionState
. One problem that arises when submitting a form directly with the action attribute is that the form will reset after submission.
Sometimes we don't want that to happen. For example, say the user submits a form with an error, but not all fields are filled incorrectly, we want to keep the user's input so they don't have to retype everything, even the correct fields.
Event Handler
To preserve the form state, we need to use an event handler to call the action, we do this to utilize the event.preventDefault()
method to prevent the form from resetting after submission, which is the default behavior of HTML forms.
'use client';
import { createTodo, State } from '@/lib/actions';
import { useActionState } from 'react';
export default function Form() {
const initialState: State = {
success: false,
message: null,
errors: {},
};
const [state, formAction, pending] = useActionState(createTodo, initialState);
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
formAction(new FormData(event.currentTarget));
}
return (
<form onSubmit={handleSubmit}>
<input name="title" type="text" />
{state.errors?.title && <p>{state.errors.title[0]}</p>}
<input name="body" type="text" disabled={pending} />
{state.errors?.body && <p>{state.errors.body[0]}</p>}
<button type="submit" disabled={pending}>
{pending ? 'Submitting...' : 'Submit'}
</button>
{state.message && <p>{state.message}</p>}
</form>
);
}
That's... basically it! now the form will not reset after submission.
Bonus: it's a good practice to wrap the formAction
call with startTransition
. This tells react that the update is non-urgent.
startTransition
From the official React docs:
"Transitions let you keep the user interface updates responsive even on slow devices. With a Transition, your UI stays responsive in the middle of a re-render.
For example, if the user clicks a tab but then change their mind and click another tab, they can do that without waiting for the first re-render to finish."
startTransition
is particularly useful in larger applications or when dealing with slower network conditions. This will lead to smoother user experience.
Let's see how we can use it:
'use client';
import { startTransition } from 'react';
//...
export default function Form() {
//...
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
startTransition(() => {
formAction(new FormData(event.currentTarget));
});
}
//...
}
Without startTransition
, if the network is slow, React might try to update the UI based on the pending state of formAction
before it completes, potentially causing visual inconsistencies.
By wrapping the formAction
call with startTransition
, we tell React to wait for the action to complete before updating the UI, leading to a smoother user experience.
There's other solution to this, but I find this one to be the most straightforward.
Hope this helps!
GitHub Repo
You can find the full code for this implementation on my GitHub.
References
- React startTransition
- Using startTransition (credit goes to leerob)
- Previous post