BlazorSignalStore 0.1.1
dotnet add package BlazorSignalStore --version 0.1.1
NuGet\Install-Package BlazorSignalStore -Version 0.1.1
<PackageReference Include="BlazorSignalStore" Version="0.1.1" />
<PackageVersion Include="BlazorSignalStore" Version="0.1.1" />
<PackageReference Include="BlazorSignalStore" />
paket add BlazorSignalStore --version 0.1.1
#r "nuget: BlazorSignalStore, 0.1.1"
#:package BlazorSignalStore@0.1.1
#addin nuget:?package=BlazorSignalStore&version=0.1.1
#tool nuget:?package=BlazorSignalStore&version=0.1.1
BlazorSignalStore
A reactive state management library for Blazor applications inspired by Angular Signals
๐ Features
- โ Angular Signals-inspired API - Familiar reactive patterns for Angular developers
- โ
React-like Hooks -
useSignal()and function syntax support for React developers - โ Automatic UI Updates - Components re-render automatically when signals change
- โ Computed Signals - Derived values that update when dependencies change
- โ Type Safe - Full TypeScript-like experience with nullable reference types
- โ Dependency Injection - Native .NET DI integration
- โ Blazor Server & WebAssembly - Works with both hosting models
- โ .NET 8+ Ready - Built for modern .NET applications
๐ฆ Installation
dotnet add package BlazorSignalStore
๐ง Quick Start
1. Register Services
// Program.cs
builder.Services.AddSignalStore<CounterStore>();
2. Create a Store
using BlazorSignalStore.Core;
public class CounterStore : StoreBase
{
public Signal<int> Count { get; } = new(0);
public Computed<string> Label { get; }
public CounterStore()
{
Label = new Computed<string>(() => $"Count: {Count.Value}", Count);
}
public void Increment() => Count.Value++;
public void Decrement() => Count.Value--;
}
3. Use in Components
@using BlazorSignalStore.Core
@inject CounterStore Store
<h3>@label()</h3>
<button @onclick="Store.Increment">+</button>
<button @onclick="Store.Decrement">-</button>
@code {
private Func<int>? count;
private Func<string>? label;
protected override void OnInitialized()
{
count = this.useSignal(Store.Count);
label = this.useSignal(Store.Label);
}
}
๐ก Core Concepts
Signals
Reactive containers that notify subscribers when their value changes:
// Create a signal
var signal = new Signal<string>("initial value");
// Subscribe to changes
var subscription = signal.Subscribe(newValue =>
Console.WriteLine($"Value changed to: {newValue}"));
// Update value (triggers subscribers)
signal.Value = "new value";
// Clean up
subscription.Dispose();
Computed Signals
Derived values that automatically recalculate when dependencies change:
var firstName = new Signal<string>("John");
var lastName = new Signal<string>("Doe");
var fullName = new Computed<string>(() => $"{firstName.Value} {lastName.Value}");
Console.WriteLine(fullName.Value); // "John Doe"
firstName.Value = "Jane";
Console.WriteLine(fullName.Value); // "Jane Doe" (automatically updated)
Blazor Integration
Use the useSignal extension method for automatic component re-rendering:
@using BlazorSignalStore.Core
@inject MyStore Store
@code {
private Func<bool>? isLoading;
protected override void OnInitialized()
{
isLoading = this.useSignal(Store.IsLoading);
// Component automatically re-renders when isLoading changes
}
}
๐ API Reference
Signal<T>
Value- Get/set the current valueSubscribe(Action<T>)- Subscribe to value changes- Implicit conversion to
Func<T>for React-like syntax
Computed<T>
Value- Get the computed value (readonly)Subscribe(Action<T>)- Subscribe to computed value changes- Constructor takes computation function and dependency signals
SignalHooks Extensions
useSignal<T>(Signal<T>)- Create a function that triggers component re-rendersuseSignal<T>(Computed<T>)- Subscribe to computed signals in components- Integrates with Blazor component lifecycle
StoreBase
- Base class for creating organized signal stores
- Inherit from this class to group related signals and computed values
๐ฏ Patterns
Store-based Architecture
public class CounterStore : StoreBase
{
public Signal<int> Count { get; } = new(0);
public Computed<string> DisplayText { get; }
public CounterStore()
{
DisplayText = new Computed<string>(() => $"Count: {Count.Value}", Count);
}
}
// In component
@inject CounterStore Store
<p>@displayText()</p>
@code {
private Func<string>? displayText;
protected override void OnInitialized()
{
displayText = this.useSignal(Store.DisplayText);
}
}
Computed Dependencies
public class ItemStore : StoreBase
{
public Signal<List<Item>> Items { get; } = new(new());
public Computed<int> ItemCount { get; }
public Computed<bool> HasItems { get; }
public ItemStore()
{
ItemCount = new Computed<int>(() => Items.Value.Count, Items);
HasItems = new Computed<bool>(() => ItemCount.Value > 0, ItemCount);
}
}
Complex State Management
public class AppStore : StoreBase
{
public Signal<User?> CurrentUser { get; } = new(null);
public Signal<bool> IsLoading { get; } = new(false);
public Computed<bool> IsAuthenticated { get; }
public AppStore()
{
IsAuthenticated = new Computed<bool>(() => CurrentUser.Value != null, CurrentUser);
}
}
๐๏ธ Architecture
BlazorSignalStore is built with:
- Signal<T> - Core reactive primitive with subscription management
- Computed<T> - Derived state with explicit dependency tracking
- useSignal Extensions - Blazor component integration methods
- StoreBase - Base class for organizing related signals
- Reflection-based updates - Automatic
StateHasChanged()calls viauseSignal - Dependency Injection - Native .NET DI integration via
AddSignalStore<T>()
๐งช Testing
The library includes comprehensive tests covering:
- Core signal functionality (11 tests) โ
- Dependency injection integration (5 tests) โ
- Computed signal behavior
- Blazor component integration
- Memory leak prevention
- Thread safety
Run tests:
dotnet test
๐ Documentation
๐ค Contributing
Contributions are welcome! Please read our contribution guidelines.
๐ License
This project is licensed under the MIT License. See LICENSE for more details.
๐ Acknowledgments
- Inspired by Angular Signals
- Built for the Blazor ecosystem
- Follows React Hooks patterns
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.AspNetCore.Components (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
v0.1.1: Fixed security vulnerabilities, improved CI/CD pipeline, and ensured cross-platform compatibility with consistent line endings.