Getting started
Quick start
A complete, reactive, time-travelable counter in three files and about twenty lines.
1. Write the store
A store is a partial class marked [BlexStore]. Fields marked
[BlexState] become reactive properties; methods marked [BlexAction] become
public wrappers that record a single named action.
using Bit.Blex;
namespace MyApp.Stores;
[BlexStore(Name = "counter")]
public partial class CounterStore
{
[BlexState] private int _count;
[BlexState] private int _step = 1;
[BlexComputed] private int ComputeDoubleCount() => Count * 2;
[BlexComputed] private bool ComputeIsEven() => Count % 2 == 0;
[BlexAction] private void OnIncrement() => Count += Step;
[BlexAction] private void OnDecrement() => Count -= Step;
}The naming conventions are mechanical, and the generator enforces them:
| You write | You get | Rule |
|---|---|---|
[BlexState] private int _count; | public int Count { get; set; } | Strip a leading underscore, capitalize. |
[BlexComputed] int ComputeDoubleCount() | public int DoubleCount { get; } | Strip Compute or Get. |
[BlexAction] void OnIncrement() | public void Increment() | Strip the On prefix. |
[BlexEffect] async Task OnLoad() | public Task Load() + LoadIsLoading + LoadError | Strip On, add the lifecycle. |
2. Register it
builder.Services.AddBitBlexServices();
builder.Services.AddBitBlexStore<CounterStore>();3. Wrap the app and use the store
<BlexProvider>
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound><p>Not found.</p></NotFound>
</Router>
</BlexProvider>@page "/counter"
@inherits BlexComponentBase
@inject CounterStore Counter
<p>Count: @Counter.Count (double: @Counter.DoubleCount)</p>
<button class="btn" @onclick="Counter.Increment">Increment</button>
<button class="btn" @onclick="Counter.Decrement">Decrement</button>
@code {
protected override void OnInitialized() => Subscribe(Counter);
}BlexComponentBase.Subscribe(store) re-renders the component whenever that
store changes, and detaches automatically when the component is disposed. That is the whole
integration - there is no StateHasChanged to call and nothing to unsubscribe.
Exactly the code above. Every click is a named action: watch the Inspector at the bottom of the page.
What you got for free
Batched renders
Every mutation inside an action is folded into one notification, so a five-field action re-renders the component once - not five times.
Named, inspectable actions
Each action carries its name and its argument payload to middleware, subscribers and the DevTools extension.
Snapshots
SerializeState() / DeserializeState() are generated, which
is what makes persistence, undo/redo and time-travel possible.
Compile-time safety
Sixteen diagnostics catch a malformed store at build time rather than emitting code that does not compile. See Diagnostics.