0Pricing
C# Academy · Урок

Управление состоянием в Blazor

Управляйте общим состоянием с помощью одиночных экземпляров служб, Fluxor или каскадного AppState, сохраняя согласованность интерфейса между компонентами.

«Управление состоянием в Blazor» — бесплатный урок C# Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 4 уроков всего.

Сложности управления состоянием в Blazor

По мере роста приложений Blazor управление состоянием, которое нужно совместно использовать во множестве не связанных друг с другом компонентов, становится сложным. Простая передача параметров через цепочку компонентов перестаёт работать; требуется централизованное реактивное хранилище состояния.

Простое состояние с помощью службы с областью действия

Самый простой способ управлять состоянием в Blazor — служба с областью действия, которая хранит состояние и предоставляет событие, чтобы компоненты могли подписываться на изменения.

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, передав ему службу состояния. Все потомки смогут обращаться к ней без явной передачи параметров через цепочку компонентов.

@* 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: шаблон Flux для Blazor

Fluxor добавляет в Blazor однонаправленный поток данных в стиле Redux: состояние → компонент → действие → редуктор → состояние. Это особенно удобно для сложных приложений.

// 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> (или примесь) автоматически подписывается на изменения состояния.

@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

Сохраняйте состояние между обновлениями страницы в браузерном localStorage с помощью взаимодействия с JavaScript. Восстанавливайте его при запуске приложения.

@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 предоставляет удобный типизированный API для браузерного localStorage, избавляя от необходимости вручную писать вызовы взаимодействия с JS.

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

Состояние URL с помощью NavigationManager

Используйте параметры запроса для хранения состояния, которое должно сохраняться после обновления страницы и поддерживать добавление в закладки. Читайте и записывайте их с помощью 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
                }));
    }
}

Практический пример: состояние корзины покупок

Полный шаблон: служба с областью действия 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

Основные выводы:

  • Служба с областью действия + событие действия: простейший вариант общего состояния, подходящий для большинства приложений
  • CascadingValue, оборачивающий корневой компонент, обеспечивает доступ во всём приложении без передачи параметров через цепочку компонентов
  • Fluxor: однонаправленный поток в стиле Redux для сложной логики состояния
  • Сохраняйте состояние в localStorage с помощью IJSRuntime или Blazored.LocalStorage
  • Состояние в URL (SupplyParameterFromQuery) для доступного добавления в закладки и обмена состоянием интерфейса
  • Всегда отменяйте подписку на события в Dispose, чтобы предотвращать утечки памяти

Часто задаваемые вопросы

Урок «Управление состоянием в Blazor» бесплатный?

Да — полный текст урока «Управление состоянием в Blazor» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 4 уроков всего.

Чему я научусь в уроке «Управление состоянием в Blazor»?

Управляйте общим состоянием с помощью одиночных экземпляров служб, Fluxor или каскадного AppState, сохраняя согласованность интерфейса между компонентами. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C# Academy?

Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Управление состоянием в Blazor»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C# Academy?

Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Модель компонентов Blazor
  2. Привязка данных и обработка событий
  3. Взаимодействие компонентов и DI
  4. Управление состоянием в Blazor
← Назад к C# Academy