Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API proposal for Declarative Model for Persistent Component and Services State #61138

Open
javiercn opened this issue Mar 24, 2025 · 0 comments
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-blazor Includes: Blazor, Razor Components
Milestone

Comments

@javiercn
Copy link
Member

Overview and Motivation

Currently, Blazor's persistent component state feature requires imperative API calls to store and retrieve state during prerendering. This proposal introduces a declarative model using attributes to simplify state persistence, allowing developers to annotate properties in components and services that should be persisted during prerendering and restored when the application becomes interactive.

Proposed API

namespace Microsoft.AspNetCore.Components
{
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public class SupplyParameterFromPersistentComponentStateAttribute : Attribute
    {
        public SupplyParameterFromPersistentComponentStateAttribute() { }
    }
}

namespace Microsoft.AspNetCore.Components.Infrastructure
{
    public class ComponentStatePersistenceManager
    {
        public ComponentStatePersistenceManager(ILogger<ComponentStatePersistenceManager> logger, IServiceProvider serviceProvider) { }

        public void SetPlatformRenderMode(IComponentRenderMode renderMode) { }
    }

    public static class RegisterPersistentComponentStateServiceCollectionExtensions
    {
        public static IServiceCollection AddPersistentServiceRegistration<TService>(
            this IServiceCollection services, 
            IComponentRenderMode componentRenderMode) { }
    }
}

namespace Microsoft.Extensions.DependencyInjection
{
    public static class SupplyParameterFromPersistentComponentStateProviderServiceCollectionExtensions
    {
        public static IServiceCollection AddSupplyValueFromPersistentComponentStateProvider(
            this IServiceCollection services) { }
    }

    public static class RazorComponentsRazorComponentBuilderExtensions
    {
        public static IRazorComponentsBuilder RegisterPersistentService<TPersistentService>(
            this IRazorComponentsBuilder builder,
            IComponentRenderMode renderMode) { }
    }
}

Usage Examples

Component State Persistence

@page "/counter"

<h1>Counter</h1>
<p>Current count: @CurrentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    [SupplyParameterFromPersistentComponentState]
    private int CurrentCount { get; set; }

    private void IncrementCount()
    {
        CurrentCount++;
    }
}

Multiple Components of the Same Type

<!-- ParentComponent.razor -->
@page "/parent"

@foreach (var element in elements)
{
    <ChildComponent @key="element.Name" />
}

<!-- ChildComponent.razor -->
<div>
    <p>Current count: @Element.CurrentCount</p>
    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
</div>

@code {
    [SupplyParameterFromPersistentComponentState]
    public State Element { get; set; }

    private void IncrementCount()
    {
        Element.CurrentCount++;
    }

    protected override void OnInitialized()
    {
        Element ??= new State();
    }

    private class State
    {
        public int CurrentCount { get; set; }
    }
}

Service State Persistence

public class CounterService
{
    [SupplyParameterFromPersistentComponentState]
    public int CurrentCount { get; set; }

    public void IncrementCount()
    {
        CurrentCount++;
    }
}

builder.Services.AddPersistentService<CounterService>(RenderMode.InteractiveAuto);

Alternative Designs

The existing imperative API for state persistence offers more flexibility for complex scenarios, but is more verbose and error-prone. The declarative model provides a simpler approach for common scenarios while still allowing fallback to the imperative API when needed.

Risk Considerations

  1. Serialization Limitations: By default, System.Text.Json is used for serialization which is not trimmer-safe. Developers must ensure that types being serialized are preserved through other means.

  2. Key Computation Constraints: The key computation algorithm only considers a subset of the component hierarchy, limiting its use in deeply nested or recursive component hierarchies.

  3. Service Persistence Scope: Only scoped services are supported for persistence, and implementations must expose the same properties for consistent serialization.

  4. Type Compatibility: Developers must ensure that types marked for persistence can be properly serialized and deserialized by the default serializer.

@javiercn javiercn added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Mar 24, 2025
@dotnet-issue-labeler dotnet-issue-labeler bot added the area-blazor Includes: Blazor, Razor Components label Mar 24, 2025
@javiercn javiercn added this to the 10.0-preview4 milestone Mar 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-blazor Includes: Blazor, Razor Components
Projects
None yet
Development

No branches or pull requests

1 participant