0Pricing
C# Academy · レッスン

Blazorの状態管理

サービスのシングルトン、Fluxor、またはカスケーディングするAppStateで共有状態を管理し、コンポーネント間でUIの一貫性を保ちます。

「Blazorの状態管理」はCoddyKit上の無料C# Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC# Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C# Academyコースには全4レッスンが含まれています。

Blazor における状態管理の課題

Blazor アプリが大きくなると、関連のない多くのコンポーネント間で共有する状態の管理は複雑になります。単純な prop drilling では対応しきれないため、集中管理されたリアクティブな状態ストアが必要です。

Scoped サービスによるシンプルな状態管理

Blazor で最もシンプルな状態管理方法は、状態を保持し、コンポーネントが変更を購読できるイベントを公開する Scoped サービスです。

public class CartState
{
    private readonly List<CartItem> _items = new();
    public IReadOnlyList<CartItem> Items => _items;
    public int Count => _items.Sum(i => i.Quantity);

    public event Action? OnChanged;

    public void Add(Product p)
    {
        var existing = _items.FirstOrDefault(i => i.ProductId == p.Id);
        if (existing is not null) existing.Quantity++;
        else _items.Add(new CartItem { ProductId = p.Id, Name = p.Name, Price = p.Price });
        OnChanged?.Invoke();
    }
}

状態変更の購読

コンポーネントは状態サービスの OnChanged イベントを購読し、再レンダリングするために StateHasChanged を呼び出します。Dispose で購読を解除することを忘れないでください。

@inject CartState Cart
@implements IDisposable

<p>Cart: @Cart.Count items</p>

@code {
    protected override void OnInitialized()
    {
        Cart.OnChanged += OnCartChanged;
    }

    private void OnCartChanged()
    {
        InvokeAsync(StateHasChanged);
    }

    public void Dispose() =>
        Cart.OnChanged -= OnCartChanged;
}

状態コンテナーとしての CascadingValue

ルートレイアウトを状態サービスを指定した CascadingValue で囲みます。すべての子孫が、明示的に prop を受け渡さなくても状態サービスにアクセスできます。

@* MainLayout.razor *@
@inject CartState Cart

<CascadingValue Value="Cart">
    @Body
</CascadingValue>

@* Any nested component: *@
@code {
    [CascadingParameter]
    public CartState Cart { get; set; } = default!;

    private void Buy(Product p) => Cart.Add(p);
}

Fluxor:Blazor の Flux パターン

Fluxor は、Redux スタイルの単方向データフローを Blazor に導入します。State → Component → Action → Reducer → State という流れで、複雑なアプリケーションに適しています。

// dotnet add package Fluxor.Blazor.Web

// State record
[FeatureState]
public record CounterState(int Count = 0);

// Actions
public record IncrementAction(int Amount = 1);

// Reducers
public static class CounterReducers
{
    [ReducerMethod]
    public static CounterState Reduce(CounterState state, IncrementAction action)
        => state with { Count = state.Count + action.Amount };
}

// Registration:
builder.Services.AddFluxor(opt =>
    opt.ScanAssemblies(typeof(Program).Assembly));

コンポーネントで Fluxor を使用する

IState<T> を注入して状態を読み取り、IDispatcher を注入してアクションをディスパッチします。InheritsFrom<FluxorComponent>(または mixin)を使用すると、状態変更の購読が自動的に行われます。

@inherits FluxorComponent
@inject IState<CounterState> CounterState
@inject IDispatcher Dispatcher

<p>Count: @CounterState.Value.Count</p>
<button @onclick="Increment">+1</button>
<button @onclick="() => Dispatcher.Dispatch(new IncrementAction(5))">+5</button>

@code {
    private void Increment()
        => Dispatcher.Dispatch(new IncrementAction(1));
}

LocalStorage への永続化

JavaScript 相互運用を介してブラウザーの localStorage を使用すると、ページを更新しても状態を保持できます。アプリの起動時に状態を復元してください。

@inject IJSRuntime JS

@code {
    private async Task SaveStateAsync()
    {
        var json = JsonSerializer.Serialize(_state);
        await JS.InvokeVoidAsync("localStorage.setItem", "appState", json);
    }

    protected override async Task OnInitializedAsync()
    {
        var json = await JS.InvokeAsync<string?>("localStorage.getItem", "appState");
        if (json is not null)
            _state = JsonSerializer.Deserialize<AppState>(json) ?? new();
    }

    private AppState _state = new();
}

Blazored.LocalStorage パッケージ

Blazored.LocalStorage パッケージは、JavaScript 相互運用の呼び出しを自分で記述しなくても、ブラウザーの localStorage を扱える、使いやすい型付き API を提供します。

// dotnet add package Blazored.LocalStorage
builder.Services.AddBlazoredLocalStorage();

@inject ILocalStorageService LocalStorage

@code {
    protected override async Task OnInitializedAsync()
    {
        _cart = await LocalStorage.GetItemAsync<List<CartItem>>("cart") ?? new();
    }

    private async Task SaveCartAsync()
    {
        await LocalStorage.SetItemAsync("cart", _cart);
    }

    private List<CartItem> _cart = new();
}

NavigationManager による URL 状態管理

ページ更新後も維持し、ブックマークできる状態には、クエリパラメーターを使用します。NavigationManager と URI ビルダーを介して、クエリパラメーターを読み書きできます。

@inject NavigationManager Nav
@page "/products"

@code {
    [SupplyParameterFromQuery] public string? Search { get; set; }
    [SupplyParameterFromQuery] public int Page { get; set; } = 1;

    private void UpdateSearch(string s)
    {
        Nav.NavigateTo(
            Nav.GetUriWithQueryParameters(
                new Dictionary<string, object?>
                {
                    [nameof(Search)] = s,
                    [nameof(Page)]   = 1
                }));
    }
}

実践例:ショッピングカートの状態管理

完全なパターンでは、イベントを持つ Scoped な CartState サービスを使用し、変更時に localStorage へ永続化して、読み込み時に復元します。

public class CartState
{
    private readonly ILocalStorageService _storage;
    private List<CartItem> _items = new();
    public event Action? OnChanged;

    public CartState(ILocalStorageService storage) => _storage = storage;

    public async Task InitAsync()
    {
        _items = await _storage.GetItemAsync<List<CartItem>>("cart") ?? new();
    }

    public async Task AddAsync(Product p)
    {
        var item = _items.FirstOrDefault(i => i.ProductId == p.Id);
        if (item is not null) item.Quantity++;
        else _items.Add(new CartItem { ProductId = p.Id, Name = p.Name });
        await _storage.SetItemAsync("cart", _items);
        OnChanged?.Invoke();
    }
}

クイックチェック

共有状態が限られているシンプルな Blazor アプリには、どの状態管理方法が最適ですか。

まとめ:Blazor の状態管理

重要なポイント:

  • Scoped サービス + Action イベント:最もシンプルな共有状態管理で、多くのアプリに適している
  • ルートを CascadingValue で囲むと、prop drilling なしでアプリ全体からアクセスできる
  • Fluxor:複雑な状態ロジック向けの Redux スタイルの単方向フロー
  • IJSRuntime または Blazored.LocalStorage を使用して状態を localStorage に永続化する
  • URL 状態(SupplyParameterFromQuery):ブックマークや共有が可能な UI 状態に使用する
  • メモリリークを防ぐため、Dispose では必ずイベントの購読を解除する

よくある質問

「Blazorの状態管理」レッスンは無料ですか?

はい。「Blazorの状態管理」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C# Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C# Academyコースには全4レッスンが含まれています。

「Blazorの状態管理」で何を学びますか?

サービスのシングルトン、Fluxor、またはカスケーディングするAppStateで共有状態を管理し、コンポーネント間でUIの一貫性を保ちます。 ブラウザで直接実行するハンズオンコードでC# Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

C# Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのC# Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「Blazorの状態管理」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このC# Academyレッスンでコードを書いて実行できますか?

はい。すべてのC# Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Blazorコンポーネントモデル
  2. データバインディングとイベント処理
  3. コンポーネント間通信とDI
  4. Blazorの状態管理
← C# Academyに戻る