Updated on

Fluxor is a state management library for Blazor that implements the Flux pattern: the same one-way data flow Redux made popular. State lives in one place, components never write to it directly, and every change happens by dispatching an action that a reducer turns into a new state object.

The payoff is traceability. Because nothing mutates state except a reducer, every change in the application has a named action behind it and can be logged, replayed, or tested in isolation. The cost is ceremony: a counter that was one field becomes a state record, an action, and a reducer.

To download the source code for this article, you can visit our GitHub repository.

What Is the Flux Pattern?

When an application grows too big, it becomes quite a challenge to update the state correctly. It is also difficult to debug why a specific state change happened. The Flux pattern is an architectural pattern designed to solve these complex state management problems on the front end. Its main goal is to make state changes transparent and predictable, thus making unit testing and debugging easier.

Let’s take a look at a typical Flux pattern flow and components:

Structure and data flow in Flux architectural pattern

Typically we’re starting from an action. Actions are events or user interactions in the application that require a state change. Next, the Dispatcher directs dispatched actions to the appropriate Store functions. The Store holds the application state. And the View is the UI that can generate new actions.

The most important principles of the Flux pattern are the unidirectional data flow and the read-only state.

Unidirectional Data Flow

Unidirectional data flow means, that data only flows in one direction in the application directly, typically from top to bottom, so from parent to children. Passing data directly back to the parent is forbidden, since we would have to have a reference to the parent which can cause serious problems. So to achieve this, the first thing we must enforce is that children cannot have access to their parents’ state. However, parents should also not modify their children’s state directly, because it can lead to unexpected state changes. They should initialize them, and then let them manage their state. So the state should be immutable.

Immutable State

In the Flux pattern, stores store the state. The store is responsible for managing state changes and emitting events when the state changes, allowing the UI to react. So if the state is immutable, children have no chance of modifying their parent’s state, nor the other way around. But then no data exchange can happen between components. That wasn’t our intention. Everyone should be able to modify their state and we need a way to allow the data flow between components.

This is where we employ actions. Using a dispatcher, we dispatch actions that contain any data necessary for the new state. Then, reducers handle these actions. A reducer’s task is to take an action and the current state, creating a new state object based on the two inputs. This ensures that the state is never modified; instead, it is always replaced.

Effects

Reducers are always pure functions, which means they cannot have a side effect, like data fetching. For example, if we need external data, we should use effects to fetch it, then dispatch an action from the effect, so reducers can update the state with the new data. The action dispatching is needed because only reducers are allowed to replace the state, effects can only execute side effects.

What Is Fluxor in Blazor?

Fluxor’s README describes it as “a zero boilerplate Flux/Redux library for Microsoft .NET”, and its Blazor package is what wires that into components.

It supplies the parts the pattern calls for so we only write our own: a store that holds state, a dispatcher that queues actions, and the wiring that discovers our reducers and effects by attribute and calls them at the right moment.

Setting it up is three lines. AddFluxor() with ScanAssemblies() in the service registrations, a StoreInitializer component at the top of the layout, and @inherits FluxorComponent on any component that reads state so it re-renders when that state changes.

Everything after that is our own types. A record for the state, a record per action, a static reducer that returns a new state, and, when a change needs to fetch something first, an effect that does the work and dispatches a second action with the result.

Diagram of the Fluxor data flow cycle: action dispatched, reducer produces new state, component re-renders, with effects branching off to handle side effects and dispatch follow-up actions.

One direction only: actions in, a new state out, nothing mutated in place.

Service Registrations

So let’s start by creating a standalone Blazor WebAssembly application — dotnet new blazorwasm — and installing version 6.11.0 of the Fluxor.Blazor.Web NuGet package. That is the template this whole article uses; if we reach for the newer Blazor Web App template instead, the store initializer goes somewhere different, and we cover that below. Now we can register Fluxor into the dependency injection container in the Program class:

builder.Services.AddFluxor(options => options.ScanAssemblies(typeof(Program).Assembly));

Here, we use the AddFluxor() method, as well as the ScanAssemblies() method to scan our assembly for reducers and effects, then add them to the dependency injection container. If the project itself is new, our article on getting started with Blazor WebAssembly covers the template this sample builds on. However, Fluxor will not register middleware automatically.

Middlewares

We haven’t talked about middleware yet, because it is not strictly part of the Flux pattern, it’s rather an extension of it. Middleware allows us to hook into various lifecycle events, and execute side effects that shouldn’t be part of an effect. For example, we aim to log every dispatched action. Writing an effect for every action to just log it, is very inefficient. So the Fluxor team introduced middleware. We can execute code before and after every action dispatch, and the middleware itself can decide if it wants to execute something on a given action’s lifecycle event.

Razor Component Setup

Next, let’s add the store initializer component to the top of the App.razor file:

<Fluxor.Blazor.Web.StoreInitializer/>

We add the StoreInitializer component to initialize and keep track of the store, which can contain many state objects. It’s also responsible for collecting unhandled exceptions and throwing them.

To learn more about components in Blazor, check out our Blazor Components with Arbitrary and Cascading Parameters article.

And the last note about integrating Fluxor into our Blazor WebAssembly application is, that every component that has a state should inherit from the Fluxor.Blazor.Web.Components.FluxorComponent class. This ensures that the component rerenders when its state changes, because in the OnInitialized() and OnInitializedAsync() methods the base class registers its event listeners. Note, that if we override the aforementioned methods it’s important to call base.OnInitialized(), otherwise our component won’t rerender and won’t reflect state changes. This is the piece that trips people up most often, and our article on component lifecycle methods in Blazor explains why that base call matters.

Store Initializer Placement in a Blazor Web App

The instruction above is correct for a standalone Blazor WebAssembly project, where App.razor is the <Router> component and everything under it is interactive. In a Blazor Web App — the default Blazor template since .NET 8 — App.razor is something else entirely: it is the HTML document, with <!DOCTYPE html>, <head> and <body>, and it is always statically rendered. A StoreInitializer placed there never becomes interactive, so the store is never initialized.

This fails silently. The page renders, the buttons respond, and the counter simply never moves — no exception, nothing in the browser console. Moving the component down into MainLayout.razor does not help either, because in a Blazor Web App the layout is statically rendered too unless a render mode is set — the same constraint applies to rendering content into a layout with sections.

Two placements do work. We can set a render mode on the router in App.razor and leave the initializer in the layout:

<Routes @rendermode="InteractiveServer" />

Or we can put <Fluxor.Blazor.Web.StoreInitializer /> inside the interactive component itself, where it inherits that page’s render mode. That version works under interactive server, interactive WebAssembly, and auto rendering alike.

Is this material useful to you? Consider subscribing and get ASP.NET Core Web API Best Practices eBook for FREE!

One warning about auto rendering: there is not one store but two, and the store lives on whichever side is currently running. The first visit renders on the server while the WebAssembly runtime downloads in the background, and every load after that runs in the browser with a brand-new, empty store — so whatever an effect fetched server-side is simply not there any more.

That said, let’s introduce the Flux pattern on the Counter page.

Using Reducers in Fluxor

The first thing we should do is to make the Counter page inherit the Fluxor.Blazor.Web.Components.FluxorComponent class:

@inherits Fluxor.Blazor.Web.Components.FluxorComponent

By adding the @inherits attribute to the top of the file, we make the Counter page a Fluxor component.

Creating the Fluxor State

Now let’s create a State folder and a Counter folder inside it, then add a new CounterState record to it:

[FeatureState]
public record CounterState
{
    public int ClickCount { get; init; }
}

Records are immutable reference types, so they are the perfect candidate to be state. Due to their immutability, we can’t modify them accidentally. We add the FeatureState attribute to the state, because Fluxor must discover and manage them. Also, we keep a parameterless constructor in state records. This is necessary because Fluxor uses reflection to instantiate them. If we were to add a constructor with parameters, it’s a good practice to create the parameterless one as private. Finally, we make the ClickCount property init only, so we will be able to use the with keyword when updating the state.

Let’s inject our state and the Dispatcher into the Counter page:

@page "/counter"
@using FluxorInBlazor.State.Counter
@using Fluxor
@inherits Fluxor.Blazor.Web.Components.FluxorComponent
@inject IState<CounterState> CounterState
@inject IDispatcher Dispatcher
<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @CounterState.Value.ClickCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private void IncrementCount()
    {
    }
}

Also, we remove the currentCount field, and use the state’s value in the markup rather than data binding in Blazor against a local field. Now we are reading data from the state, but we are not updating it yet. Let’s fix it by creating the action and the reducer.

Creating the Action and the Reducer

In the Counter folder let’s create an Actions folder and add an IncrementCounterAction record:

public record IncrementCounterAction;

Actions should also be immutable since we do not want them to change after we dispatch them. Therefore using records here is also appropriate. Because we only want to increment the counter by one at a time, we won’t need any properties for our action, but we could have.

Next, let’s create a static Reducers class inside the Counter folder:

[ReducerMethod]
public static CounterState ReduceIncrementCounterAction(CounterState state, IncrementCounterAction action)
{
    return new() {ClickCount = state.ClickCount + 1};
}

Here, We add the ReducerMethod attribute to the ReduceIncrementCounterAction() method, so Fluxor can discover it.

We make reducers static because they only take the previous state and the action as inputs and output a new state. If they are static, we cannot use dependency injection in them, thus we won’t trigger accidental side effects. Reducers always take the current state as their first argument and the action they handle as the second one. Fluxor will infer from the method signatures which one it should call for a specific action.

Finally, we return a new state with the incremented ClickCount.

Using the Dispatcher

The last step to make this work is to dispatch the IncrementCounterAction in the Counter page’s IncrementCount() method:

private void IncrementCount()
{
    Dispatcher.Dispatch(new IncrementCounterAction());
}

We use the Dispatch() method, passing in a new instance of our IncrementCounterAction record.

Notice, that action dispatching is not async code, because behind the scenes it just appends our action to a Queue and could process it at a later time. So regardless of synchronous code, Fluxor may not handle actions immediately.

Until now we created a CounterState record, an IncrementCounterAction, and a ReduceIncrementCounterAction() reducer method, and wired them up. Let’s now navigate to the Counter page in the application and click the button. The current count should be incremented by one. Behind the scenes, the dispatcher queued our action, and Fluxor forwarded it to our reducer. Which in turn, created a new state, with ClickCount incremented by one. Then the store replaced the state, and the counter component rerendered, displaying the state change to us. This is the basic Flux workflow, dispatch an action, reduce it into state, and rerender the page that relies on that state.

But how can we fetch data from the server if we can’t use async, or dependency injection in reducers? Let’s take a look at it using effects on the Weather page.

Using Effects in Fluxor

As on the Counter page, let’s start by inheriting the Weather page from Fluxor.Blazor.Web.Components.FluxorComponent:

@inherits Fluxor.Blazor.Web.Components.FluxorComponent

Creating the Fluxor State

Still similar to the Counter page, let’s create a Weather folder inside the State folder and add a WeatherState record to it:

[FeatureState]
public record WeatherState
{
    public bool IsLoading { get; init; }
    public IEnumerable<WeatherForecast> Forecasts { get; init; } = new List<WeatherForecast>();
}

Here, we introduce two properties on the state, to help avoid null pointer exceptions. When IsLoading is true, we will display a loading text, and we won’t access the Forecasts property. However, even if we accidentally access it while loading, it won’t throw a null pointer exception, because it has a default value of an empty list. And when the loading is finished, we set IsLoading to false, to indicate that the page can render the forecast list.

Let’s inject the state into the page, and update the razor markup to read data from the state:

@inject IState<WeatherState> WeatherState
@inject IDispatcher Dispatcher

@if (WeatherState.Value.IsLoading)
{
    <p>
        <em>Loading...</em>
    </p>
}
else
{
    @foreach (var forecast in WeatherState.Value.Forecasts)
    {
        @* code removed for brevity *@
        <tr>
            <td>@forecast.Date.ToShortDateString()</td>
            <td>@forecast.TemperatureC</td>
            <td>@forecast.TemperatureF</td>
            <td>@forecast.Summary</td>
        </tr>
    }
}

Here, we inject the state and the Dispatcher and remove the forecasts field. Instead, we use data from the state and do not send an HTTP request from the OnInitialized() method.

Creating the Actions

Now we will need two actions: one to initialize the data fetching, and another to dispatch when we have the data. So let’s create a FetchDataAction and a DataFetchedAction record in a new Actions folder inside Weather:

public record FetchDataAction;

The FetchDataAction is a simple record without any properties, because we use it as a signal to start fetching the data:

public record DataFetchedAction(IEnumerable<Pages.Weather.WeatherForecast> Forecasts);

Is this material useful to you? Consider subscribing and get ASP.NET Core Web API Best Practices eBook for FREE!

On the other hand, we add a Forecasts property to the DataFetchedAction record because it will contain the fetched data.

Creating the Reducer

Let’s create a Reducers class and add a reducer that will initialize the state when the FetchDataAction is dispatched:

public static class Reducers
{
    [ReducerMethod(typeof(FetchDataAction))]
    public static WeatherState ReduceFetchDataAction(WeatherState state) => new() { IsLoading = true };
}

Here, we do not need any data from the action, so instead of adding it to the parameter list, we can pass its type as a parameter to the ReducerMethod attribute. We successfully evaded an unused method parameter warning.

Creating the Effects

Now let’s create an Effects class and add an effect method for this same action because Fluxor will execute both reducers and effects if they match an action, we don’t have to dispatch different actions for reducers and effects:

public class Effects(HttpClient http)
{
    [EffectMethod(typeof(FetchDataAction))]
    public async Task HandleFetchDataAction(IDispatcher dispatcher)
    {
        var forecasts = await http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json") 
                        ?? Array.Empty<WeatherForecast>();
        dispatcher.Dispatch(new DataFetchedAction(forecasts));
    }
}

We utilize primary constructors to inject the HttpClient into the Effects class. Next, we decorate the HandleFetchDataAction() method with the EffectMethod attribute, which can be used very similarly to the ReducerMethod attribute. Effect methods always get the dispatcher as a parameter, so when data fetching is finished, we can dispatch the DataFetchedAction. For the request itself, our guide to calling an API with HttpClient in Blazor covers the setup an effect relies on.

Let’s create a reducer for this action too:

[ReducerMethod]
public static WeatherState ReduceDataFetchedAction(WeatherState state, DataFetchedAction action)
{
    return new() {IsLoading = false, Forecasts = action.Forecasts};
}

We take the data from the action and create a new state with it, that has IsLoading set to false, so the page will know that forecasts are available to display.

Finally, let’s dispatch the FetchDataAction in the Weather page’s OnInitialized() method:

protected override void OnInitialized()
{
    base.OnInitialized();
    Dispatcher.Dispatch(new FetchDataAction());
}

Remember to always call the base.OnInitialized() method to make the page rerender on state changes.

Now, when navigating to the weather page, the first reducer briefly displays the loading screen for a split second before the effect, and the second reducer finishes execution and shows the data.

Using Middleware in Fluxor

In the Fluxor introductory section, we mentioned middleware, and how they can execute code before and after all actions, or just specific ones. Let’s look at an example logging middleware, to see how they work.

To learn more about middleware please read our article ASP.NET Core Middleware – Creating Flexible Application Flows.

Let’s start by creating a Middlewares folder inside the State folder and add a LoggingMiddleware class inside:

public class LoggingMiddleware : Middleware
{
}

We inherit the LoggingMiddleware from Fluxor’s Middleware base class and we will override the specific lifecycle methods we need.

Fluxor’s Middleware Lifecycles

Let’s look at the lifecycle of Fluxor’s middleware, which has five steps:

Middleware LifecycleDescription
Task InitializeAsync(IStore store)This is the first method that gets called during a middleware's lifecycle. We can use it to get a reference to the store and perform other necessary initialization.
void AfterInitializeAllMiddlewares()This method is called when all other middlewares finish their initialization, and the store is also ready to be used.
bool MayDispatchAction(object action)This method is called after the Dispatcher.Dispatch() method is called, but before the action is passed to the effects and reducers. Here, we can terminate the action dispatch, and completely prevent all effects and reducers from handling the action. If all middlewares return true, Fluxor dispatches the action; however, if even one returns false, it discards it.
void BeforeDispatch(object action)If the action didn't get discarded by a middleware, then before actually dispatching it this method will be called.
void AfterDispatch(object action)Finally, after reducers and effects finish execution, this method is called so we are notified about successful action handling.

Logging Implementation

For our logging middleware, let’s override the BeforeDispatch() method:

public override void BeforeDispatch(object action)
{
    Console.WriteLine($"Before dispatching action {action.GetType().Name}");
}

We simply log the name of the action to the console. As we already mentioned, middleware will not be registered automatically, so let’s add it by calling the AddMiddleware() method inside the AddFluxor() extension:

builder.Services.AddFluxor(options => options.ScanAssemblies(typeof(Program).Assembly)
    .AddMiddleware<LoggingMiddleware>());

Let’s reload the page and inspect the console output:

Before dispatching action StoreInitializedAction
Before dispatching action FetchDataAction
Before dispatching action DataFetchedAction

StoreInitializedAction is a built-in action Fluxor uses to notify its components that the store is ready. Also, we see in the log our two actions from the weather page in the order we dispatch them.

Do We Need Fluxor for Blazor State Management?

Usually not. Blazor has three simpler options, and most applications never outgrow them.

A component that owns its own state needs nothing. State shared between a parent and its children is a cascading value or a parameter. State shared across unrelated pages is a scoped service holding the data and raising an event when it changes: perhaps fifteen lines, and it is what most teams end up writing.

Fluxor earns its ceremony when the history of the state matters as much as the value itself. Many components writing to the same data, changes arriving from several directions, a bug report that says “it was wrong after I did these four things”: that is when a named action per change, and a log of them, stops being overhead.

The honest test: if we cannot name the actions in our application, we do not need a dispatcher for them.

Microsoft’s own state-management overview says “Blazor doesn’t provide comprehensive, opinionated state management” and names Flux, Redux and MobX as the third-party answers, which is exactly the decision this section is about.

PieceWhat it isRule
StateA record marked [FeatureState]Immutable; replaced, never modified
ActionA record describing something that happenedImmutable; carries the data the change needs
ReducerA static method marked [ReducerMethod]Pure: takes state and action, returns new state
EffectA method marked [EffectMethod]Where side effects live: HTTP, storage, timers
DispatcherIDispatcher, injectedQueues actions; not immediate, not async
MiddlewareA class deriving from MiddlewareCross-cutting hooks around every dispatch
StoreManaged by StoreInitializerHolds every feature state; added once in the layout

Conclusion

We discussed the Flux pattern itself, its core principles, and why and when should we use it.

Then we looked at how Fluxor implements this pattern, discovered some of its inner workings, and integrated it into a Blazor WebAssembly application.

After, we did a deep dive into reducers, effects, and middleware. We made the whole Blazor template use Fluxor for state management.

All in all, the most important takeaway is, to know when we need Fluxor, and when is the application simple enough to use easier state management mechanics. But if we need Fluxor, then we will now have the confidence to utilize its powerful state management features.

Tested with .NET 10.0.10.