Partilhar via


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

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

Um requisito típico durante Blazor o desenvolvimento do aplicativo é o compartilhamento do estado entre os componentes:

  • Pai para filho: um componente pai passa o estado para um componente filho usando parâmetros.
  • De filho para pai: um componente filho permite a vinculação de dados ao seu estado ou fornece o estado por meio de callbacks.
  • Pai para descendentes: um pai compartilha o estado 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 do aplicativo configurados.
  • Por circuito: o estado é partilhado para um circuito específico usando serviços de estado de aplicação com escopo.

O estado persistente pode precisar sobreviver à atualização das páginas, à retomada dos circuitos e à pré-renderização. O estado geralmente requer gerenciamento central, rastreamento e testes. Os locais e técnicas para o estado persistente são altamente variáveis.

Blazor não fornece uma gestão estatal abrangente e opinativa. Produtos e serviços de contentores de estado de terceiros que funcionam perfeitamente com Blazor, como Flux, Redux e MobX, satisfazem praticamente qualquer requisito de aplicação.

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. Exemplos de estado do usuário modelado na URL incluem:

  • A ID de uma entidade visualizada.
  • O número de página atual numa grelha paginada.

O conteúdo da barra de endereço do navegador é mantido:

  • 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 informações sobre como definir padrões de URL com a @page diretiva, veja o roteamento do ASP.NET CoreBlazor.

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

Os componentes aninhados normalmente ligam dados usando ligação encadeada , conforme descrito em ASP.NET Core Blazor vinculação de dados. Os componentes aninhados e não aninhados 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 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, mas o aninhamento não é necessário para que esta 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 em componentes. O exemplo não armazena dados sem um desenvolvimento adicional. Para armazenamento persistente de dados, o contêiner de estado deve adotar um mecanismo de armazenamento subjacente que sobreviva quando a memória do navegador é 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 (arquivoProgram):

builder.Services.AddSingleton<StateContainer>();

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

builder.Services.AddScoped<StateContainer>();

Aplicativos do lado do servidor (Startup.ConfigureServices do 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 associados ao OnChange são desinscritos nos métodos Dispose, que são chamados pelo framework quando os componentes são eliminados. Para obter mais informações, consulte o descarte de componentes 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.

Os valores em cascata a nível de raiz com um CascadingValueSource<TValue> permitem que o componente assinante Razor seja notificado das alterações nos valores em cascata. Para obter mais informações e um exemplo funcional, consulte o exemplo NotifyingDalek em ASP.NET Core Blazor valores e parâmetros em cascata.

Permite modificações de estado fora do contexto de sincronização de Blazor

Ao usar um serviço de gestão de estado personalizado em que deseja suportar modificação de estado a partir de fora do contexto síncrono do Blazor(por exemplo, a partir de um temporizador ou de um serviço em segundo plano), todos os componentes consumidores devem envolver 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 do Blazor, o seguinte erro é lançado:

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

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