AspNetCore.Docs/aspnetcore/blazor/includes/state-container.md

3.5 KiB

Nested components typically bind data using chained bind as described in xref:blazor/components/data-binding. Nested and unnested components can share access to data using a registered in-memory state container. A custom state container class can use an assignable xref:System.Action to notify components in different parts of the app of state changes. In the following example:

  • A pair of components uses a state container to track a property.
  • One component in the following example is nested in the other component, but nesting isn't required for this approach to work.

[!IMPORTANT] The example in this section demonstrates how to create an in-memory state container service, register the service, and use the service in components. The example doesn't persist data without further development. For persistent storage of data, the state container must adopt an underlying storage mechanism that survives when browser memory is cleared. This can be accomplished with localStorage/sessionStorage or some other technology.

StateContainer.cs:

public class StateContainer
{
    private string? savedString;

    public string Property
    {
        get => savedString ?? string.Empty;
        set
        {
            savedString = value;
            NotifyStateChanged();
        }
    }

    public event Action? OnChange;

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

In Program.cs (Blazor WebAssembly):

builder.Services.AddSingleton<StateContainer>();

In Program.cs (Blazor Server) in ASP.NET Core 6.0 or later:

builder.Services.AddScoped<StateContainer>();

In Startup.ConfigureServices (Blazor Server) in versions of ASP.NET Core earlier than 6.0:

services.AddScoped<StateContainer>();

Shared/Nested.razor:

@implements IDisposable
@inject StateContainer StateContainer

<h2>Nested component</h2>

<p>Nested component Property: <b>@StateContainer.Property</b></p>

<p>
    <button @onclick="ChangePropertyValue">
        Change the Property from the Nested component
    </button>
</p>

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

    private void ChangePropertyValue()
    {
        StateContainer.Property = 
            $"New value set in the Nested component: {DateTime.Now}";
    }

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

Pages/StateContainerExample.razor:

@page "/state-container-example"
@implements IDisposable
@inject StateContainer StateContainer

<h1>State Container Example component</h1>

<p>State Container component Property: <b>@StateContainer.Property</b></p>

<p>
    <button @onclick="ChangePropertyValue">
        Change the Property from the State Container Example component
    </button>
</p>

<Nested />

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

    private void ChangePropertyValue()
    {
        StateContainer.Property = "New value set in the State " +
            $"Container Example component: {DateTime.Now}";
    }

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

The preceding components implement xref:System.IDisposable, and the OnChange delegates are unsubscribed in the Dispose methods, which are called by the framework when the components are disposed. For more information, see xref:blazor/components/lifecycle#component-disposal-with-idisposable-and-iasyncdisposable.