TimeWarp.State
TimeWarp.State (previously known as Blazor-State ) is a fully asynchronous state management library for Blazor applications, leveraging the MediatR pipeline to implement the Flux pattern. It handles both Reducers and Effects consistently using async Handlers, simplifying the management of asynchronous operations throughout your app.
By utilizing the MediatR pipeline, TimeWarp.State enables a flexible, middleware-driven architecture for managing state, similar to the request-processing pipeline in ASP.NET. This approach allows developers to inject custom behaviors, such as logging, validation, and caching, directly into the state management flow.
In addition to the core library, we offer TimeWarp.State.Plus, which extends the functionality with enhanced middleware, components, and tools to further streamline state management in complex Blazor applications.
Please see the GitHub Site for source and filing of issues.
Installation
dotnet add package TimeWarp.State
dotnet add package TimeWarp.State.Plus
Check out the latest NuGet packages on the TimeWarp Enterprises NuGet page.
Getting Started
I recommend the tutorial for a step-by-step guide to building a Blazor app with TimeWarp.State.
👉 Official documentation.
The TimeWarp.State Architecture
Store 1..* State
TimeWarp.State implements a single Store
with a collection of State
s.
To access a state you can either inherit from the TimeWarpStateComponent and use
Store.GetState<YourState>()
or move the GetState functionality into your component
protected T GetState<T>()
{
Type stateType = typeof(T);
Subscriptions.Add(stateType, this);
return Store.GetState<T>();
}
Pipeline
TimeWarp.State utilizes the MediatR pipeline which allows for middleware integration
by registering an interface with the dependency injection container 1.
TimeWarp.State provides the extension method 2 , AddTimeWarpState
, which registers behaviors on the pipeline.
The interfaces available to extend the Pipeline are:
IPipelineBehavior
IRequestPreProcessor
IRequestPostProcessor
IStreamPipelineBehavior
You can add functionality to the pipeline by implementing and registering one of these interfaces.
See the timewarp-architecture EventStreamBehavior
for an example.
Behaviors/Middleware
TimeWarp.State ships with the following default middleware.
CloneStateBehavior
To ensure your application is in a known good state the CloneStateBehavior
creates a clone of the State
prior to processing the Action
.
If any exception occurs during the processing of the Action
the state is rolled back.
RenderSubscriptionsPostProcessor
When a component accesses State
, a subscription is added.
The RenderSubscriptionsPostProcessor
will iterate over these subscriptions and re-render those components that return true for ShouldReRender.
So you don't have to worry about where to call StateHasChanged
and still have the ability to finely control re-rendering.
JavaScript Interop
TimeWarp.State also uses the same "Command Pattern" for JavaScript interoperability. The JavaScript creates a request and dispatches it to Blazor where it is added to the pipeline. Handlers on the Blazor side can callback to the JavaScript side if needed.
ReduxDevToolsPostProcessor
Note
Disabled by default. This should be disabled in production as it consumes significant resources.
One of the nice features of redux is the developer tools 3. This processor implements the integration of these developer tools.
Terminology
The pattern used by TimeWarp.State and MediatR has been around for many years and goes by different names. We list various related terms here and Bold indicates the term used in TimeWarp.State.
Signals/Actions/Requests/Commands/
In Redux they call them "Action".
In UML they are "Signal".
In Command Pattern they are "Command"
In MediatR they are Request
In TimeWarp.State we call them Actions
when they are handled on the Client and Requests
if they handled on the Server.
Reducer/Handler/Executor
This is the code that processes the Request/Action
and returns the Response
.
In Redux they call them "Reducer".
In Command Pattern we call them "Executor".
In MediatR they are Handler
.
In TimeWarp.State we call them Handler
.
Feature
A Feature is the collection of the code needed to implement a particular Vertical Slice of the application.
On the server side we use the same architecture, where the Features contain
Endpoint
, Handler
, Request
, Response
, etc...
Each endpoint maps the HTTP Request to the Request
object and then sends the Request
on to the mediator pipeline.
The Handler
acts on the Request
and returns a Response
.
Server side follows the Request
in Response
out pattern.
A Feature Folder on the client side will contain an Action
and the Handler
and any corresponding files needed for this feature.
The Handler
acts on the Action
and updates the corresponding State
.
Client side follows the Action
in new State
out pattern.
PureFunctions vs NonPureFunctions
TimeWarp.State does not distinguish between these. As they are processed via the pipeline the same. Thus, async calls to fetch data, send emails, or just update local state are implemented in the same manner. Although the developer should be aware when Handlers have side effects and if the developer chose they could mark the Requests as such. For example IActionWithSideEffect
Acknowledgements
Jimmy Bogard (MediatR).
Jimmy is an amazing developer and knowledge sharer.
Through his course at 11x Engieering,
his many blog posts on Los Techies and now JimmyBogard.com,
I have learned a great amount.
Peter Morris and I have been friends for many years.
He is an amazing developer and a person who has taught me a great deal.
Not surprisingly, Pete and I think a lot alike.
We both, independently, started working on our own State Management
components. Although I started first. :P
Pete's component attempts to solve most of the same problems.
TimeWarp.State draws on the strengths of a proven async pipeline in MediatR, where as Fluxor
implements its own middleware.
If TimeWarp.State does not meet your needs be sure to checkout Fluxor.