dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.2k stars 9.94k forks source link

PersistComponentState inconsistent behavior #51583

Closed Markz878 closed 10 months ago

Markz878 commented 10 months ago

Is there an existing issue for this?

Describe the bug

I migrated a Blazor hosted WASM app to use .NET 8, and noticed that my database was called twice when navigating to a page that uses WASM render mode and state persistence from a server rendered page. When the page is navigated directly to (or browser is refreshed when on the page), the state persist mechanism works as expected, the database is hit only once, and the markup for state persistence (Blazor-WebAssembly-Component-State) disappears after WASM kicks in.

But after I navigate to another server rendered page and back, the OnInitialized method runs twice without using the state persistance mechanism. This seems like a bug, and puts extra load on the database.

Expected Behavior

I would expect that the state persistence mechanism would behave similarly when navigating between server rendered and interactive WASM pages, and when navigating directly to the interactive page.

Steps To Reproduce

Create new Blazor 8 project with WASM interactivity on per page basis and with sample pages.

Put this code on the Counter page on the Client project:

@page "/counter"
@implements IDisposable
@attribute [RenderModeInteractiveWebAssembly]
<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

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

@code {
    private int currentCount = 0;
    [Inject] public required PersistentComponentState PersistentComponentState { get; init; }
    private PersistingComponentStateSubscription stateSubscription;
    protected override void OnInitialized()
    {
        stateSubscription = PersistentComponentState.RegisterOnPersisting(PersistData);
        if (PersistentComponentState.TryTakeFromJson(nameof(currentCount), out int count))
        {
            currentCount = count;
            Console.WriteLine("Took count from STORAGE");
        }
        else
        {
            currentCount = 5;
            Console.WriteLine("Took count from SERVICE");
        }
    }

    private Task PersistData()
    {
        PersistentComponentState.PersistAsJson(nameof(currentCount), currentCount);
        return Task.CompletedTask;
    }

    private void IncrementCount()
    {
        currentCount++;
    }

    public void Dispose()
    {
        stateSubscription.Dispose();
    }
}

Run the app and observe the browser Console tab and the application terminal.

When the Counter page is first navigated to, the terminal says "Took count from SERVICE", and the browser says "Took count from STORAGE", which is how it's supposed to work. Same thing happens if the browser is refreshed. Also, the "Blazor-WebAssembly-Component-State" markup disappears.

But when navigating to Home and back to Counter, both say "Took count from SERVICE". This means that the state persistence was not used, and the "database" was hit twice. Also, the "Blazor-WebAssembly-Component-State" markup stays on the page.

Exceptions (if any)

No response

.NET Version

8.0.100-rc.2.23502.2

Anything else?

No response

javiercn commented 10 months ago

@Markz878 thanks for contacting us.

Persistent component state works in the same way as it does for Blazor Server and WebAssembly. It is only supported as part of the initial render of the components when a runtime (server or webassembly) starts, but it does not work with successive enhanced page navigations.

javiercn commented 10 months ago

I've filed https://github.com/dotnet/aspnetcore/issues/51584 with more details for the feature request.

Markz878 commented 10 months ago

Okay great, any chance this would come in .NET 8?

Markz878 commented 10 months ago

This could also lead to developers (at least me) making some mistakes about assuming which branch of the TryTakeFromJson condition is run at which point. In my app I initialize a SignalR connection in the true branch (cause I thought it would always run on the WASM client side), and was confused when the connection was sometimes initialized and sometimes not.

ghost commented 10 months ago

This issue has been resolved and has not had any activity for 1 day. It will be closed for housekeeping purposes.

See our Issue Management Policies for more information.