本文へスキップ
BecomeCoder

Blazorコース · 第6章 データとサービス連携 · レッスン22

状態管理 ― サービスでアプリ全体の状態を共有する

ローカル実施

導入

「ログイン中のユーザー」「カートの中身」のように、複数のコンポーネントで共有したい状態は、パラメータのバケツリレーでは大変です。共有状態をサービスにまとめ、DIで各コンポーネントに配り、変化をeventで通知するのが定番です。

説明

状態を持つサービスを作り、変化時にイベントを発火します。各コンポーネントは@injectで受け取り、そのイベントを購読して再描画します。

// 共有する状態をサービスに(Program.cs で AddSingleton<CartService>())
public class CartService
{
    private readonly List<string> _items = new();
    public IReadOnlyList<string> Items => _items;

    public event Action? OnChange;      // 変化を購読者に通知するイベント

    public void Add(string item)
    {
        _items.Add(item);
        OnChange?.Invoke();             // 全購読者に「変わった」と知らせる
    }
}
@inject CartService Cart
@implements IDisposable

<p>カート: @Cart.Items.Count 個</p>
<button @onclick='() => Cart.Add("商品A")'>追加</button>

@code {
    protected override void OnInitialized() => Cart.OnChange += StateHasChanged;
    public void Dispose() => Cart.OnChange -= StateHasChanged;
}
flowchart TB
    S["CartService(状態+OnChange)<br/>AddSingleton で1つ"] --> C1["ヘッダー(個数表示)"]
    S --> C2["商品一覧(追加ボタン)"]
    S --> C3["カートページ(中身)"]
  • 共有状態はサービスクラスにまとめ、AddSingletonで1つ登録する
  • 変化はevent Action OnChangeで通知し、各コンポーネントはOnChange += StateHasChangedで再描画
  • 購読したらIDisposableDisposeで必ず解除する(メモリリーク防止)
  • これはObserverパターンそのもの。学んだ設計原則が実アプリで統合される

まとめ

  • 共有状態はサービス化してDIで配る
  • 変化はeventで通知し、StateHasChangedで再描画
  • 購読解除(IDisposable)を忘れない

次回: どうしてもJavaScriptが必要なときの相互運用です。