A lightweight, reactive state management library for Blazor applications, designed to be simple, intuitive, and powerful. Built on top of System.Reactive, it provides a clean way to manage shared application state and communicate between components.
- Centralized State Management: Keep your application's state in one place, making it predictable and easy to debug.
- Reactive API: Use observables to listen for state changes and automatically update your UI.
- Component Communication: A built-in Event Aggregator allows decoupled components to communicate seamlessly.
- Scoped or Singleton: Easily configure state to be per-user (scoped) or global (singleton).
- Boilerplate Reduction:
StateComponentBaseprovides a convenient starting point for your components, handling dependency injection and subscription management automatically.
This project is not yet on NuGet. To use it, you will need to clone this repository and reference the EasyState.Blazor project.
In your Blazor application's Program.cs, register the EasyState services. You can choose between scoped or singleton lifetimes.
For most Blazor Server applications, scoped is the correct choice. This creates a new state container for each user circuit.
// Program.cs
using EasyState.Blazor;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
// Add EasyState for per-user state management
builder.Services.AddStateManagement();
// ... other services
var app = builder.Build();If you need a single state shared across all users of your application, use AddGlobalStateManagement.
// Use for application-wide global state
builder.Services.AddGlobalStateManagement();Create simple POCO classes to represent the pieces of your application state.
// States/CounterState.cs
public class CounterState
{
public int CurrentCount { get; set; } = 0;
}Inherit from StateComponentBase to get easy access to state and event management helpers.
In this example, we'll create a simple counter component.
@page "/counter"
@inherits StateComponentBase
<h3>Counter</h3>
<p>Current count: @CurrentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int CurrentCount { get; set; }
protected override void OnInitialized()
{
// ObserveState subscribes to changes in CounterState.
// It automatically updates the component when the state changes.
ObserveState<CounterState>(state =>
{
CurrentCount = state.CurrentCount;
});
}
private void IncrementCount()
{
// UpdateState provides a safe way to modify the state.
UpdateState<CounterState>(state =>
{
state.CurrentCount++;
});
}
}How it works:
StateComponentBaseinjects theIAppStateservice.OnInitialized()sets up a subscription usingObserveState<CounterState>(). Whenever theCounterStateis modified anywhere in the app, the provided lambda is executed, and the component's UI is automatically re-rendered.IncrementCount()callsUpdateState<CounterState>(), which gets the current state, applies the update action, and notifies all subscribers.
Use the IEventAggregator to send messages between components that don't have a direct parent-child relationship.
First, define the event message:
// Events/NotificationEvent.cs
public class NotificationEvent
{
public string Message { get; set; }
public DateTime Timestamp { get; set; }
}Sender Component
This component publishes a NotificationEvent.
@inherits StateComponentBase
<button @onclick="SendNotification">Send Notification</button>
@code {
private void SendNotification()
{
PublishEvent(new NotificationEvent
{
Message = "Hello from the sender!",
Timestamp = DateTime.UtcNow
});
}
}Receiver Component
This component subscribes to NotificationEvent and displays the message.
@inherits StateComponentBase
@implements IDisposable
<h3>Notifications</h3>
@if (!string.IsNullOrEmpty(LastMessage))
{
<p>@LastMessage</p>
}
else
{
<p>Waiting for a notification...</p>
}
@code {
private string LastMessage { get; set; } = "";
protected override void OnInitialized()
{
// Subscribe to the event. The handler will be called
// each time a NotificationEvent is published.
SubscribeEvent<NotificationEvent>(HandleNotification);
}
private void HandleNotification(NotificationEvent e)
{
LastMessage = $"'{e.Message}' at {e.Timestamp:T}";
}
}StateComponentBase automatically handles unsubscriptions when the component is disposed.
The core service for managing state objects.
T GetState<T>(): Retrieves the current instance of a state object, creating it if it doesn't exist.void SetState<T>(T state): Overwrites a state object completely.void UpdateState<T>(Action<T> updateAction): Performs a thread-safe update on a state object.IObservable<T> ObserveState<T>(): Returns an observable that emits the latest state and then any subsequent changes.
A service for pub/sub messaging.
void Publish<TEvent>(TEvent eventData): Publishes an event to all subscribers.IObservable<TEvent> Subscribe<TEvent>(): Returns an observable for listening to events of a specific type.
An abstract base component that simplifies interaction with the state and event services. It manages subscriptions and ensures the UI is updated in response to changes. All subscriptions are automatically disposed of when the component is destroyed.