Skip to content

CoderFoundry/PersistentStateDemo

Repository files navigation

Persistent Component State Demo

This repository demonstrates the usage of the new [SupplyParameterFromPersistentComponentState] attribute added in .NET 10 Preview.

Note: The .NET team has mentioned that this attribute should be given a shorter name for the final release of .NET 10.

Watch the demo on YouTube:
Persistent Component State Walkthrough

What is Persistent Component State?

When building a Blazor Web App, pages are prerendered by default before interactivity is established. This means that Blazor initializes components rendered on a page server-side to deliver initial HTML to the browser while the components become interactive.

As a result, the OnInitialized or OnParametersSet methods are called twice for prerendered components: once during prerendering and again when they are created in an interactive render mode. This can result in fetching data twice and a UI "flash" when the prerendered data is lost, since state is not maintained by default when a component changes render mode.

Persistent Component State allows Blazor to serialize a component's state into the prerendered HTML so that the same data from pre-rendering can be reused when the component becomes interactive.

Code Examples

A typical example of fetching data without persisting the state can be found in ProductView.razor and viewed in the application on the home page or at /shop/none.

.NET 8 or .NET 9

Persistent component state can be achieved by injecting the PersistentComponentState service and registering a callback to persist state used by the component. An example can be found in PersistentProductView.razor.

[Inject] PersistentComponentState PersistentState { get; set; }
List<Product>? products;
List<Category>? categories;

string productsKey = $"{nameof(PersistentProductView)}-{nameof(products)}";
string categoriesKey = $"{nameof(PersistentProductView)}-{nameof(categories)}";
PersistingComponentStateSubscription persistentStateSubscription;

// method to explicitly persist state
private Task PersistComponentState()
{
    PersistentState.PersistAsJson(productsKey, products);
    PersistentState.PersistAsJson(categoriesKey, categories);

    return Task.CompletedTask;
}

// method to explicitly restore state
private void RestoreComponentState()
{
    if (PersistentState.TryTakeFromJson(productsKey, out List<Product>? persistedProducts))
    {
        products = persistedProducts;
    }

    if (PersistentState.TryTakeFromJson(categoriesKey, out List<Category>? persistedCategories))
    {
        categories = persistedCategories;
    }
}

// required to dispose of registered callback.
// Components must explicitly implement IDisposable
public void Dispose()
{
    persistentStateSubscription.Dispose();
}

protected override async Task OnInitializedAsync()
{
    // attempt to restore state before fetching data
    RestoreComponentState();

    products ??= await GalacticRelicsService.GetProductsAsync();
    categories ??= await GalacticRelicsService.GetCategoriesAsync();

    // register callback to persist data
    persistentStateSubscription = PersistentState.RegisterOnPersisting(PersistComponentState);
}

.NET 10+

In .NET 10, the [SupplyParameterFromPersistentComponentState] attribute was introduced to automatically persist component state without the ceremony involved in the previous example. An example can be found in PersistentAttributeProductView.razor.

[SupplyParameterFromPersistentComponentState]
public List<Product>? Products { get; set; }

[SupplyParameterFromPersistentComponentState]
public List<Category>? Categories { get; set; }

protected override async Task OnInitializedAsync()
{
    // if state was persisted, these values will not be null
    Products ??= await GalacticRelicsService.GetProductsAsync();
    Categories ??= await GalacticRelicsService.GetCategoriesAsync();
}

Demo the Application

Prerequisites

  • .NET 10 Preview 5 SDK or newer
  • IDE with .NET 10 support, e.g. Visual Studio 2022 Preview or VS Code with C# Extension preview installed

Running

Notes

The shop pages include a RenderModeIndicator component displayed in the top right corner of the page to easily see whether the page is displayed from static rendering (prerendered) or InteractiveWebAssembly. On the home page or /shop/none, you will notice a UI flash when the render mode changes and the products must be re-fetched from the API. In the other pages, there should be no flash when the render mode changes because the state has been persisted.

This can be seen by setting a breakpoint in your debugger in the OnInitializedAsync method of each product view component. This method is called twice - once during prerendering and again during interactive rendering - and the values in products and categories can be inspected each time. In all cases, these values will be null on the first execution of the method. In the second execution, the components which persist component state will have values for products and categories before they are fetched by the GalacticRelicsService.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published