Compartilhar via


Visão geral do gerenciamento de estado do ASP.NET Core Blazor

Este artigo e os outros artigos neste nó descrevem abordagens comuns para manter os dados de um usuário (estado) enquanto eles usam um aplicativo e entre sessões do navegador, inclusive durante a pré-geração de servidor.

Um requisito típico durante Blazor o desenvolvimento de aplicativos é compartilhar o estado entre componentes:

  • Pai para filho: um componente pai passa o estado para um componente filho usando parâmetros.
  • Filho para pai: um componente filho permite a associação de dados ao seu estado ou fornece estado por meio de retornos de chamada.
  • Pai para descendentes: um estado de compartilhamento pai com todos os seus descendentes usando valores em cascata.
  • Em todo o aplicativo: o estado é compartilhado em todo o aplicativo usando serviços de estado de aplicativo configurados.
  • Por circuito: o estado é compartilhado para um circuito específico usando serviços de estado de aplicativo com escopo.

O estado persistente pode precisar sobreviver a atualizações de páginas, circuitos retomados e pré-geração. O estado geralmente requer gerenciamento central, acompanhamento e teste. Os locais e técnicas para manter o estado são altamente variáveis.

Blazor não fornece gerenciamento de estado abrangente e opinativo. Os produtos e serviços de contêiner de estado de terceiros que funcionam perfeitamente com Blazor, como Flux, Redux e MobX, atendem praticamente qualquer requisito de aplicativo.

O restante deste artigo discute estratégias gerais de gerenciamento de estado para qualquer tipo de Blazor aplicativo.

Gerenciamento de estado usando a URL

Para dados transitórios que representam o estado de navegação, modele os dados como parte da URL. Os exemplos de estado do usuário modelados na URL incluem:

  • O ID de uma entidade visualizada.
  • O número da página atual em uma grade paginada.

O conteúdo da barra de endereços do navegador é retido:

  • Se o usuário recarregar manualmente a página.
  • Somente cenários do lado do servidor: se o servidor Web ficar indisponível e o usuário for forçado a recarregar a página para se conectar a um servidor diferente.

Para obter informações sobre como definir padrões de URL com a @page diretiva, consulte ASP.NET Core Blazor routing.

Serviço de contêiner de estado na memória

Os componentes aninhados normalmente associam dados usando a associação encadeada, conforme descrito em Associação de dados Blazor do ASP.NET Core. Os componentes aninhados e não conectados podem compartilhar o acesso aos dados usando um contêiner de estado na memória registrado. Uma classe de contêiner de estado personalizado pode usar um Action atribuível para notificar componentes em diferentes partes do aplicativo sobre as alterações de estado. No exemplo a seguir:

  • Um par de componentes usa um contêiner de estado para rastrear uma propriedade.
  • Um componente no exemplo a seguir está aninhado no outro componente, mas o aninhamento não é necessário para que essa abordagem funcione.

Importante

O exemplo nesta seção demonstra como criar um serviço de contêiner de estado na memória, registrar o serviço e usar o serviço nos componentes. O exemplo não armazena dados sem um desenvolvimento adicional. Para o armazenamento persistente de dados, o contêiner de estado deve adotar um mecanismo de armazenamento subjacente que sobreviva, quando a memória do navegador for limpa. Isso pode ser feito com localStorage/sessionStorage ou alguma outra tecnologia.

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();
}

Aplicativos do lado do cliente (arquivo Program):

builder.Services.AddSingleton<StateContainer>();

Aplicativos do lado do servidor (arquivo Program, ASP.NET Core no .NET 6 ou posterior):

builder.Services.AddScoped<StateContainer>();

Aplicativos do lado do servidor (Startup.ConfigureServices de Startup.cs, normalmente no .NET 6 ou anterior):

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;
    }
}

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;
    }
}

Os componentes anteriores implementam IDisposable, e os delegados OnChange não são assinados nos métodos Dispose, que são chamados pela estrutura quando os componentes são descartados. Para obter mais informações, consulte descarte de componente do ASP.NET CoreRazor.

Valores e parâmetros em cascata

Use valores e parâmetros em cascata para gerenciar o estado fluindo dados de um componente ancestral Razor para componentes descendentes:

  • Para consumir o estado em muitos componentes.
  • Se houver apenas um objeto de estado de nível superior para persistir.

Valores em cascata de nível raiz com um CascadingValueSource<TValue> permitem Razor notificações de assinante de componente de valores em cascata alterados. Para obter mais informações e um exemplo funcional, consulte o NotifyingDalek exemplo em parâmetros e valores em cascata do ASP.NET CoreBlazor.

Dar suporte a modificações de estado de fora Blazordo contexto de sincronização

Ao usar um serviço de gerenciamento de estado personalizado em que você deseja dar suporte a modificações de estado fora do contexto de sincronização de Blazor(por exemplo, de um temporizador ou um serviço em segundo plano), todos os componentes consumidores devem encapsular a chamada StateHasChanged em ComponentBase.InvokeAsync. Isso garante que a notificação de alteração seja tratada no contexto de sincronização do renderizador.

Quando o serviço de gerenciamento de estado não chama StateHasChanged no contexto de sincronização de Blazor, o erro a seguir é gerado:

System.InvalidOperationException: 'O thread atual não está associado ao Dispatcher. Use o InvokeAsync() para alternar a execução para o Dispatcher ao acionar a renderização ou o estado do componente.

Para obter mais informações e um exemplo de como solucionar esse erro, consulte ASP.NET Core Razor renderização de componentes.