次の方法で共有


ASP.NET Core Blazor 状態管理の概要

この記事とこのノードの他の記事では、ユーザーがアプリを使用している間や、サーバーのプリレンダリング中など、ブラウザー セッション間でユーザーのデータ (状態) を維持するための一般的な方法について説明します。

Blazorアプリ開発時の一般的な要件は、コンポーネント間で状態を共有することです。

  • 親から子: 親コンポーネントは、パラメーターを使用して子コンポーネントに状態を渡します。
  • 子から親へ: 子コンポーネントは、その状態へのデータ バインディングを有効にするか、コールバックを介して状態を提供します。
  • 親から子孫へ: 親は、カスケード値を使用して、すべての子孫と状態を共有します。
  • アプリ全体: 状態は、構成されたアプリ状態サービスを使用してアプリ全体で共有されます。
  • 回線ごと: 状態は、スコープ付きアプリ状態サービスを使用して特定の回線で共有されます。

永続化された状態では、ページの更新、再開された回線、およびプリレンダリングを維持する必要がある場合があります。 状態は、多くの場合、集中管理、追跡、テストを必要とします。 状態を保持するための場所と手法は非常に可変です。

Blazor は、包括的で意見の豊富な状態管理を提供しません。 Flux、Redux、MobX など、 Blazorとシームレスに連携するサードパーティの状態コンテナー製品とサービスは、事実上すべてのアプリ要件を満たします。

この記事の残りの部分では、あらゆる種類の Blazor アプリの一般的な状態管理戦略について説明します。

URL を使用した状態管理

ナビゲーションの状態を表わす一時的なデータについては、URL の一部としてデータをモデル化します。 たとえば、次のようなユーザー状態が URL でモデル化されます。

  • 表示されるエンティティの ID。
  • ページ付きグリッドでの現在のページ番号。

次の場合、ブラウザーのアドレス バーのコンテンツが保持されます。

  • ユーザーがページを手動で再読み込みした。
  • サーバー側のシナリオのみ: Web サーバーが使用できなくなった場合、ユーザーは別のサーバーに接続するためにページの再読み込みを強制されます。

@page ディレクティブを使用して URL パターンを定義する方法については、「ASP.NET Core Blazor ルーティング」を参照してください。

メモリ内状態コンテナー サービス

入れ子になったコンポーネントは通常、「」で説明されているように、"チェーン バインド" を使用してデータをバインドします。Blazor 入れ子になったコンポーネントと入れ子になっていないコンポーネントでは、登録済みのメモリ内状態コンテナーを使用してデータへのアクセスを共有できます。 カスタムの状態コンテナー クラスでは、割り当て可能な Action を使用して、状態変更のアプリのさまざまな部分でコンポーネントに通知できます。 次に例を示します。

  • コンポーネントのペアでは、状態コンテナーを使用してプロパティを追跡します。
  • 次の例の 1 つのコンポーネントは、他のコンポーネントで入れ子になっていますが、この方法を使用するには入れ子である必要はありません。

Von Bedeutung

このセクションの例では、メモリ内状態コンテナー サービスを作成し、サービスを登録し、コンポーネントでサービスを使用する方法を示します。 この例では、それ以上開発しないとデータは保持されません。 データの永続ストレージの場合、状態コンテナーは、ブラウザー メモリがクリアされたときに存続する基になるストレージ メカニズムを採用する必要があります。 これは、localStorage/sessionStorage またはその他のテクノロジを使用して実現できます。

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

クライアント側アプリ (Program ファイル):

builder.Services.AddSingleton<StateContainer>();

サーバー側アプリ (Program ファイル、.NET 6.0 以降の ASP.NET Core):

builder.Services.AddScoped<StateContainer>();

サーバー側アプリケーション (Startup.ConfigureServicesStartup.cs、通常は .NET 6 以前):

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

前のコンポーネントによって IDisposable が実装され、OnChange メソッドで Dispose デリゲートがサブスクライブ解除されます。このメソッドは、コンポーネントが破棄されるときにフレームワークによって呼び出されます。 詳細については、ASP.NET Core Razor コンポーネントの破棄を参照してください。

値とパラメーターの連続性

カスケード値とパラメーターを使用して、先祖Razorコンポーネントから子孫コンポーネントにデータをフローすることで状態を管理します。

  • 多くのコンポーネントで状態を使用する。
  • 最上位の状態オブジェクトを 1 つだけ保持する場合。

CascadingValueSource<TValue>を持つルートレベルのカスケード値によって、変更されたカスケード値を受信するコンポーネントサブスクライバーは通知を許可されます。 詳細と実際の例については、ASP.NET Core NotifyingDalekカスケード値とパラメーターのBlazorの例を参照してください。

Blazorの同期コンテキストの外部からの状態変更をサポートする

外部からの状態変更をサポートしたい場合(例えば、タイマーやバックグラウンドサービスから)に、Blazorの同期コンテキスト外でカスタム状態管理サービスを使用するときは、すべてのコンポーネントが StateHasChanged の呼び出しを ComponentBase.InvokeAsyncでラップする必要があります。 これにより、レンダラーの同期コンテキストで変更通知が処理されます。

状態管理サービスが StateHasChanged の同期コンテキストで Blazor を呼び出さない場合は、次のエラーがスローされます。

System.InvalidOperationException: "現在のスレッドは Dispatcher に関連付けられていません。 レンダリングまたはコンポーネントの状態をトリガーするとき、InvokeAsync() を使用して Dispatcher に実行を切り替えます"

このエラーに対処する方法の詳しい情報と例については、「ASP.NET Core Razor コンポーネントのレンダリング」をご覧ください。