Advanced Blazor State Management Using Fluxor, part 6 - Persisting State
Tags: csharp, blazor
Published: 2021-03-14 20:58:39

>This is the sixth in a short series of blog posts where I will go beyond the introductory level and dig a bit deeper into using the Fluxor library in a Blazor Wasm project.

### Persisting State

In [a comment on the previous post in this series](https://dev.to/padnom/comment/1cd3b), a reader observes that when the application is reloaded, the application state is lost. They then ask how to go about saving the application state so that it remains even after the page is reloaded.

There are undoubtedly many ways to accomplish this; In this post I'll demonstrate one technique.

I'll be using the browser's [localStorage API](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) as the mechanism for storing the state of the Counter and the Weather features, but this could work just as well using the [IndexedDB API](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) or even calling off to some web API endpoint to save and retrieve state in a database.

To begin, let's note that the localStorage API is a JavaScript API, and isn't directly available to be called by our Blazor code. We need to either write our own JSInterop code to interact with localStorage, or we can take advantage of a library somebody else has already written. I'm going to use the excellent [Blazored.LocalStorage](https://github.com/Blazored/LocalStorage).

To install it into our `Client` project, we start with the NuGet package:

```
Install-Package Blazored.LocalStorage
```

And configure it in the `Program.cs` file:

```csharp
using Blazored.LocalStorage;

...

builder.Services.AddBlazoredLocalStorage(config => 
{
    config.JsonSerializerOptions.WriteIndented = true;
});
```

Let's start by saving the `CounterState` to localStorage.

To do so, I want to have an `EffectMethod` that subscribes to an `Action` instructing it to store the State, and which then `Dispatches` a Success or Failure Action that my application can then respond to.

For the `EffectMethod` to have access to the Blazored.LocalStorage service, we need to inject it into the `CounterEffects` class. We should also define a unique string to act as the localStorage key for the CounterState. 

```csharp
private readonly ILocalStorageService _localStorageService;
private const string CounterStatePersistenceName = "BlazorWithFluxor_CounterState";

public CounterEffects(ILocalStorageService localStorageService)
{
    _localStorageService = localStorageService;
}
```

Now I can add an `EffectMethod` that can be handed a `CounterState` for the `localStorageService` to persist. The service will handle serializing the State for us.

```csharp
[EffectMethod]
public async Task PersistState(CounterPersistStateAction action, IDispatcher dispatcher) 
{
    try
    {
        await _localStorageService.SetItemAsync(CounterStatePersistenceName, action.CounterState);
        dispatcher.Dispatch(new CounterPersistStateSuccessAction());
    }
    catch (Exception ex) 
    {
        dispatcher.Dispatch(new CounterPersistStateFailureAction(ex.Message));
    }
}

```

This means we need 3 new actions:

```csharp
public class CounterPersistStateAction
{
    public CounterState CounterState { get; }
    public CounterPersistStateAction(CounterState counterState)
    {
        CounterState = counterState;
    }
}
public class CounterPersistStateSuccessAction { }
public class CounterPersistStateFailureAction
{
    public string ErrorMessage { get; }
    public CounterPersistStateFailureAction(string errorMessage)
    {
        ErrorMessage = errorMessage;
    }
}

```

We can then repeat the process for Loading the state, and for completeness we may also want to Clear the State.

```csharp
[EffectMethod(typeof(CounterLoadStateAction))]
public async Task LoadState(IDispatcher dispatcher)
{
    try
    {
        var counterState = await _localStorageService.GetItemAsync<CounterState>(CounterStatePersistenceName);
        if (counterState is not null) 
        {
            dispatcher.Dispatch(new CounterSetStateAction(counterState));
            dispatcher.Dispatch(new CounterLoadStateSuccessAction());
        }
    }
    catch (Exception ex)
    {
        dispatcher.Dispatch(new CounterLoadStateFailureAction(ex.Message));
    }
}

[EffectMethod(typeof(CounterClearStateAction))]
public async Task ClearState(IDispatcher dispatcher)
{
    try
    {
        await _localStorageService.RemoveItemAsync(CounterStatePersistenceName);
        dispatcher.Dispatch(new CounterSetStateAction(new CounterState { CurrentCount = 0 }));
        dispatcher.Dispatch(new CounterClearStateSuccessAction());
    }
    catch (Exception ex)
    {
        dispatcher.Dispatch(new CounterClearStateFailureAction(ex.Message));
    }
}
```

These EffectMethods need to affect the value of the CounterState, so we need a ReducerMethod to handle the `CounterSetStateAction`.

```csharp
[ReducerMethod]
public static CounterState OnCounterSetState(CounterState state, CounterSetStateAction action) 
{
    return action.CounterState;
}
```

With these in place, we are able to persist and retrieve the `CounterState` whenever is appropriate for our application by `Dispatching` the corresponding `Actions`.

For this scenario, let's assume we want to read and apply the CounterState when the application loads. Let's accomplish this by creating a Razor Component that has the responsibility of retrieving persisted state. we create a component named `StateLoader.razor` and place in it the following code:

```csharp
@inherits FluxorComponent

@using BlazorWithFluxor.Client.Features.Counter.Store

@inject IDispatcher Dispatcher

@code {
    protected override void OnAfterRender(bool firstRender)
    {
        base.OnAfterRender(firstRender);

        if (firstRender)
        {
            Dispatcher.Dispatch(new CounterLoadStateAction());
        }
    }
}
```

And we can now reference this component in the `MainLayout.razor` file so that it's loaded at application start.

```html
@inherits LayoutComponentBase

<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>

    <div class="main">
        <div class="top-row px-4">
            <a href="http://blazor.net" target="_blank" class="ml-md-auto">About</a>
        </div>

        <div class="content px-4">
            @Body
        </div>
    </div>
</div>

<BlazoredToasts Position="ToastPosition.TopRight" Timeout="15" />

<StateLoader />

```

This will load the CounterState from localStorage if it exists. Let's now give the Counter.razor component a way to Save, Load, and Clear the State by adding a few buttons beside the existing Increment button:

```html
<button class="btn btn-primary" @onclick="IncrementCount">Increment</button>
<button class="btn btn-outline-primary" @onclick="SaveCount">Save</button>
<button class="btn btn-outline-info" @onclick="LoadCount">Load</button>
<button class="btn btn-outline-danger" @onclick="ClearCount">Clear</button>
```

and their click handlers:

```csharp
private void SaveCount()
{
    Dispatcher.Dispatch(new CounterPersistStateAction(CounterState.Value));
}

private void LoadCount()
{
    Dispatcher.Dispatch(new CounterLoadStateAction());
}

private void ClearCount()
{
    Dispatcher.Dispatch(new CounterClearStateAction());
}
```

And with that, we're set.

>**Note**: For demonstration purposes I have hooked in the Blazored.Toast component to pop up a Toast message whenever the CounterState is saved or retrieved, as demonstrated in the previous post in this series. For brevity's sake I'm not including that code in this post, but you can find it all in [the project's repository on github](https://github.com/eric-king/BlazorWithFluxor).

When the `Save` button is clicked, we can see the CounterState is saved to the browser's Local Storage:

![Demonstration of state Save](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oizxltwsyylwhok2wsjr.gif)

And if the page is reloaded after the state has been saved, the application will load the saved state:

![Demonstration of state Load on app Load](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jt4xyx8v04e6r6y8zwwv.gif)

When the `Load` button is clicked, state is retrieved from storage and the Store's state is replaced with it:

![Demonstration of state Load on button click](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ixn0lu3affl2q64x0by8.gif)

And the `Clear` button will remove the state from storage and reset the current count:

![Demonstration of state Clear](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pr0t0k8k4wqter7u10ax.gif)

One thing to note about Local Storage is that it's not secure, and it can be easily and directly manipulated right in the browser's developer tools:

![Demonstration of state manually altering state data in Local Storage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x6ah6ya9bovo971yrt65.gif)

Which means it can be manipulated in a way that results in badly formatted data:

![Demonstration of state manually altering state data in Local Storage with bad data](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1tz2lwzgfx97a9h692hq.gif)

So if you need to persist the state in a secure manner, you'll need to use something other than Local Storage.

If we repeat the same set of actions for the `WeatherStore`, we can see that `Blazored.LocalStorage` can handle more complicated objects too, including collections of objects like the `Forecasts` array.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iorw4zcrgqdofqjx75cq.gif)

Please leave a comment if you're enjoying this series or if you have suggestions for more posts.

As always, happy coding!