Preventing double form submission in a Blazor application

 
 
  • Gérald Barré

To submit a form, a user clicks on the submit button or press enter. You can then process the form and do something such as saving data and redirecting to another page. If the user quickly clicks on the button twice you may process the form multiple times simultaneously. If the form inserts a new record in a database, you'll get 2 records instead of a single one. You may want to prevent the user from submitting the form twice.

A solution is to disable the submit button while processing the form. However, in Blazor Server, event processing is asynchronous. Indeed, the message is serialized and sent to the server which handles it and sends back the new UI to the client. This means there is a short delay between the click and disabling the button. So, the user can click twice or more on the button before it is disabled. You could disable the button in JavaScript, so it is instant. But this adds complexity to the application because of JS interop.

Blazor uses a synchronization context to emulate a single-threaded environment so that it closely matches the WebAssembly model in the browser, which is single-threaded. Even if you use Blazor Server, the events are processed like if there was only one thread thanks to the synchronization context. This means you don't need any synchronization primitives, such as lock, to process only a single event at a time. To solve the double form submit problem, you can use a Boolean value to indicate whether the submit event is being processed. So, even if the button is not yet disabled in the UI because of network delay, you can prevent handling the event twice by checking this Boolean value.

Razor
<EditForm Model="customer" OnValidSubmit="OnSubmit">
    <InputText @bind-Value="customer.Name" />

    @* 👇 Disable the button while submitting the form *@
    <button type="submit" disabled=@isSubmitting>Save</button>
</EditForm>

@code{
    private Customer customer = new customer();
    bool isSubmitting;

    async Task OnSubmit()
    {
        // We don't need any synchronization primitive (lock) as there is only one thread that processes the events thanks to Blazor synchronization context

        // Maybe the button is not yet disable, be sure to not reprocess the event in this case
        if (isSubmitting)
            return;

        isSubmitting = true;
        try
        {
            await Task.Delay(1000); // TODO do actual work here
        }
        finally
        {
            isSubmitting = false;
        }
    }
}

#Additional resources

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee💖 Sponsor on GitHub