Canceling background tasks when a user navigates away from a Blazor component

 
 
  • Gérald Barré

In an application, you don't want to waste resources. When an ongoing operation is not needed anymore, you should cancel it. For instance, if your application downloads data to show them in a view, and the user navigates away from this view, you should cancel the download.

In a .NET application, the way to cancel operation is to use a CancellationToken. You can create a CancellationToken using the CancellationTokenSource. Once the Blazor component is removed from the page, you can call the Cancel method to stop the ongoing operations.

According to the Blazor component lifecycle, you can call the Cancel method in the IDisposable.Dispose() method.

SampleComponent.razor (Razor)
@page "/fetchdata"
@using System.Threading
@implements IDisposable
@inject HttpClient Http

... todo content

@code {
    // 👇 Create the CancellationTokenSource
    private CancellationTokenSource cts = new CancellationTokenSource();
    private WeatherForecast[] forecasts;

    protected override async Task OnInitializedAsync()
    {
        // 👇 Use the CancellationToken for the http call using cts.Token
        forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json", cts.Token);
    }

     // 👇 Cancel the token
    public void Dispose()
    {
        cts.Cancel();
        cts.Dispose();
    }

    public class WeatherForecast
    {
        public DateTime Date { get; set; }

        public int TemperatureC { get; set; }

        public string Summary { get; set; }

        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    }
}

#Using a base component

Instead of adding a CancellationTokenSource to all components manually, you can create a base component that expose a CancellationToken and use this base component automatically in all components of the project.

ComponentWithCancellationToken.cs (C#)
public abstract class ComponentWithCancellationToken : ComponentBase, IDisposable
{
    private CancellationTokenSource? _cancellationTokenSource;

    protected CancellationToken ComponentDetached => (_cancellationTokenSource ??= new()).Token;

    public virtual void Dispose()
    {
        if (_cancellationTokenSource != null)
        {
            _cancellationTokenSource.Cancel();
            _cancellationTokenSource.Dispose();
            _cancellationTokenSource = null;
        }
    }
}

Then, edit the _Imports.razor files with:

_Imports.razor (Razor)
@inherits ComponentWithCancellationToken
@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using MyBlazorApp
@using MyBlazorApp.Shared

You can now use the ComponentDetached property in your components:

SampleComponent.razor (Razor)
@inject HttpClient Http

... todo content

@code {
    private WeatherForecast[] forecasts;

    protected override async Task OnInitializedAsync()
    {
        forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json", ComponentDetached);
    }
}

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