'Can the state of the Counter in the example Blazor project be preserved between page switches?

In the default example project for both server-side Blazor and WebAssembly Blazor projects, the Counter example resets to 0 every time you move between the pages. However, on the ASP.NET React example project, the Counter does not reset between page switches.

Is there a way for a component like the Counter to preserve state between page navigation in Blazor (at least for the WebAssembly project that isn't making any server calls)?

enter image description here



Solution 1:[1]

It looks like this exact scenario is discussed in https://docs.microsoft.com/en-us/aspnet/core/blazor/state-management?view=aspnetcore-3.0#client-side-in-the-browser

Seems Blazor just doesn't handle it out-of-the-box, but you just need to use localStorage or sessionStorage.

Using the Blazor.Extensions.Storage NuGet package (https://github.com/BlazorExtensions/Storage):

@page "/counter"

@inject ISessionStorage SessionStorage
@using Blazor.Extensions.Storage.Interfaces

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    protected override async Task OnInitializedAsync()
    {
        currentCount = await SessionStorage.GetItem<int>("counter");
    }

    private async void IncrementCount()
    {
        currentCount++;
        await SessionStorage.SetItem<int>("counter", currentCount);
    }
}

Solution 2:[2]

I cover this in my article (works for server side Blazor as well as client side (WebAssembly) Blazor): Implementing State Management In Blazor

Add a class called CounterState.cs using the following code:

    public class CounterState
    {
        public int CurrentCount { get; set; }
    }

Register this class, using Dependency Injection, in the the Startup.cs:

services.AddScoped<CounterState>();

Add the following code to the top of the .razor code page:

@inject CounterState CounterState

Change the following code:

<p>Current count: @currentCount</p>

To:

<p>Current count: @CounterState.CurrentCount</p>

Finally, change the code section to the following:

@code {
    void IncrementCount()
    {
        // ** SESSION STATE
        int CurrentCount = CounterState.CurrentCount;
        CurrentCount++;
        // Set Current count on the Session State object
        CounterState.CurrentCount = CurrentCount;
    }
}

Solution 3:[3]

For server side Blazor if you want the counter value to be persisted/updated on all tabs/clients you could simply store it in a static variable and rerender if the value changes.

enter image description here

@page "/counter"

<h1>Static Counter</h1>

<p>Current static count: <span style="font-size:42px">@currentCount</span></p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    public static int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;

        StaticCount.FireUpdate();
    }

    protected override void OnInitialized()
    {
        base.OnInitialized();

        StaticCount.NewCounterValue += OnNewCounterValue;
    }

    void OnNewCounterValue(object sender, EventArgs e)
    {
        InvokeAsync(() =>
        {
            StateHasChanged();
        });
    }

    public void Dispose()
    {
        StaticCount.NewCounterValue -= OnNewCounterValue;
    }

}

@code{
    public static class StaticCount
    {
        public static event EventHandler NewCounterValue;

        public static void FireUpdate()
        {
            NewCounterValue?.Invoke(null, new EventArgs());
        }
    }
}

You could also do this with a (singleton) service and even persist the value in a database if you want to keep the value between server restarts.

Solution 4:[4]

I wrote an answer about Blazor state management that looks into this here:

https://stackoverflow.com/a/65609519/3850405

My recommendation would be an in-memory state container service.

Example:

StateContainer.cs

public class StateContainer
{
    public string Property { get; set; } = "Initial value from StateContainer";

    public event Action OnChange;

    public void SetProperty(string value)
    {
        Property = value;
        NotifyStateChanged();
    }

    private void NotifyStateChanged() => OnChange?.Invoke();
}

Program.Main (Blazor WebAssembly):

builder.Services.AddSingleton<StateContainer>();

Startup.ConfigureServices (Blazor Server):

services.AddSingleton<StateContainer>();

Pages/Component1.razor

@page "/Component1"
@inject StateContainer StateContainer
@implements IDisposable

<h1>Component 1</h1>

<p>Component 1 Property: <b>@StateContainer.Property</b></p>

<p>
    <button @onclick="ChangePropertyValue">Change Property from Component 1</button>
</p>

<Component2 />

@code {
    protected override void OnInitialized()
    {
        StateContainer.OnChange += StateHasChanged;
    }

    private void ChangePropertyValue()
    {
        StateContainer.SetProperty($"New value set in Component 1 {DateTime.Now}");
    }

    public void Dispose()
    {
        StateContainer.OnChange -= StateHasChanged;
    }
}

Shared/Component2.razor

@inject StateContainer StateContainer
@implements IDisposable

<h2>Component 2</h2>

<p>Component 2 Property: <b>@StateContainer.Property</b></p>

<p>
    <button @onclick="ChangePropertyValue">Change Property from Component 2</button>
</p>

@code {
    protected override void OnInitialized()
    {
        StateContainer.OnChange += StateHasChanged;
    }

    private void ChangePropertyValue()
    {
        StateContainer.SetProperty($"New value set in Component 2 {DateTime.Now}");
    }

    public void Dispose()
    {
        StateContainer.OnChange -= StateHasChanged;
    }
}

https://docs.microsoft.com/en-us/aspnet/core/blazor/state-management?view=aspnetcore-5.0&pivots=webassembly#in-memory-state-container-service-wasm

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3
Solution 4 Ogglas