Sane Loading State Management in .NET Frontends (Blazor + WPF)
Tags: dotnet, blazor, tutorial
Published: 2023-02-10 17:54:38

## Scenario
In any kind of real-world application, you will need to eventually display a loading spinner. Usually pretty early on and usually pretty often. Any time a page is loading, any time you click a button or interact with any other type of UI control.

## The Problem
Have you ever seen this kind of code and progression?
It begins very simple.

```cs
IsLoading = true;
var data = await CallExternalService();
IsLoading = false;
```
Now let's add a standard programming pattern with early returns. 
3/8 lines of actual code here are reserved for loading state management.

```cs
IsLoading = true;
var result = await CallExternalService();
if (result is "cool")
{
    var differentResult = await GetDataFromSomewhereElse();
    IsLoading = false;
    return differentResult;
}
IsLoading = false;
return result;
```
Can't forget error handling.
3/7 lines of actual code here are reserved for loading state management.

```cs
try
{
    IsLoading = true;
    var data = await CallExternalService();
    IsLoading = false;
}
catch (Exception ex)
{
    // log..
    IsLoading = false;
}
```

If you combine the two and add more real-life code than just a single call to an external service it gets even messier. I know I have seen this type of code hundreds of times. You probably did too.

## The Solution

What we will end up with is this.

```cs
private async Task GetDataFromSomwhere()
{
    using var _ = DisplayLoading();
    var result = await CallExternalService();
}
```
The first line here sets IsLoading = true at the beginning of the method and IsLoading = false at the end of the method. No matter what happens or where exactly you exit the method.

If you just want the code, here it is in a blazor page.

```cs
public partial class BasePage: ComponentBase, IBasePage
{
    public bool IsLoading { get; set; } = false;
    protected LoadingHelper DisplayLoading() => new LoadingHelper(this);
}

public class LoadingHelper : IDisposable
{
    private IBasePage basePage;

    public LoadingHelper(IBasePage basePage)
    {
        this.basePage = basePage;
        basePage.IsLoading = true;
    }

    public void Dispose()
    {
        basePage.IsLoading = false;
    }
}
```

## How it works

The LoadingHelper implements `IDisposable`. `IDisposable` is typically implemented by classes that need to do their own cleanup. It's usually heavier classes like SqlConnections or file access.
The `Dispose()` method runs when the class gets cleaned up by the `GarbageCollector`.

When the `BasePage` creates an instance of the `LoadingHelper` in a local method, that instance will get cleaned up at the end of the method. Always.
So this is where I ultimately set IsLoading = false.

I am using some c# syntax sugar to get the code as terse as possible.
This is actually the same code.

```cs
private async Task GetDataFromSomewhere()
{
    using (var loadingHelper = DisplayLoading())
    {
        var result = await CallExternalService();
    }
}
```
The first one is the _ discard operator. If you don't need access to a local variable, you can simply assign it to _.
The second one is the `using declaration`. A `using declaration` is scoped to the surrounding scope. In this case the method.

## How I got there

My first attempt was just writing a wrapping method. Something like this. 

```cs
    private T WrapInLoading<T>(Func<T> func)
    {
        IsLoading = true;
        var res = func.Invoke();
        IsLoading = false;
        return res;
    }
```

This leads to client code looking like this

```cs
private async Task GetDataV5()
{
    await WrapInLoading(async () =>
    {
        //...
        //...
        var data = await CallExternalService();
    });
}
```

This is not terrible. However not that pretty either. It also can get pretty complicated if you want synchronous and asynchronous code with and without return values. Handling void, T, Task and Task<T> is certainly possible, however not simple.

Ultimately this just "looked like" the standard using syntax to me and I dreamed up a syntax I would like to have.
Something like
```cs
using(Loading){

}
```
I like what I ended up with even more and it actually compiles.
It even avoids the extra level of nesting.

What I would love to have now is `using(Loading);` however, I don't think it is possible with the current c# language features.