State Management - Do you use the AppState pattern?
The AppState pattern is one of the simplest State Management patterns to implement with Blazor WebAssembly.
To start implementing the pattern, declare a class that describes the collection of fields that represents the state of a page, a form, or a model.
Here are some basic example state objects:
public class Counter{public int Counter { get; set; }}public class RegistrationForm{public Guid FormId { get; set; }public string EmailAddress { get; set; }public string GivenName { get; set; }public string Surname { get; set; }public string JobTitle { get; set; }}public class TimesheetEntry{public int Id { get; set; }public int ClientId { get; set; }public string ClientName { get; set; }public int ProjectId { get; set; }public string ProjectName { get; set; }public decimal HourlyRate { get; set; }public DateTime StartTime { get; set; }public DateTime EndTime { get; set; }public string Notes { get; set; }}public class Timesheet{public int Id { get; set; }public string UserName { get; set; }public TimesheetEntry[] Entries { get; set; }}
Typically, these state objects would be hydrated from user input or a request to the backend API. In order for us to use this state object, we first need to register it as an injectable service (in Program.cs):
builder.Services.AddScoped<Counter>();builder.Services.AddScoped<RegistrationForm>();builder.Services.AddScoped<Timesheet>();
Once registered, we can use the @inject directive to inject the object into a page or component:
@page "/counterWithState"@* Inject our CounterState and use it in the view and/or code section *@@inject Counter _state<PageTitle>Counter</PageTitle>@* we can reference the state object in the Razor markup *@<p>Current count: @_state.Count</p>@* Note: Due to user interaction, the page will refresh and show updated state value, even though we have not called StateHasChanged *@<button type="button" @onclick="IncrementCount">Click me</button><button type="button" @onclick="Reset">Reset</button>@code {private void IncrementCount(){// we can modify the state object in the @code section++_state.Count;}private void Reset(){_state.Count = 0;}}
Alternatively if we are using code-behind (separate .razor and .razor.cs files), then we can use the [Inject] attribute to inject the object as a parameter for the code-behind class.
Note: Constructor based injection is not supported for Blazor code-behind. Only Parameter based injection is supported.
public partial class Counter : ComponentBase{[Inject]public Counter State { get; set; }private void IncrementCount(){++_state.Count;}private void Reset(){_state.Count = 0;}}
❌ Drawbacks of basic AppState pattern
- We are unable to react to state changes made to the state object by other components
- We can modify the state but the page will not refresh to reflect the change
- We need to call
StateHasChanged()manually when we modify the state
✅ Benefits of basic AppState pattern
- Implementation is trivial - register, inject, consume
- Works for very basic scenarios - especially if there are basic user interactions and basic state mutations directly on the
@code(akaViewModel) section