Michael Bradvica joined Saxony Partners in 2020 as a Software Engineer on our app dev team. Previously, he has worked as a Full-Stack .NET Developer for technology services firms. You can read more of his thought leadership at MichaelBradvica.com – from which the blog post below has been adapted.

This blog post is part one in a three-part series detailing the steps to achieve in-memory state in Blazor.

Blazor In-Memory State Management: A Step-by-Step Guide, Part 1

In traditional web applications, state is stored in the database, cookies, or URL. While all those options are still viable in Blazor, we have a new technique available called in-memory state. Our state can be updated via publishing an event from a component, to which any other component in our application may subscribe. The barrier to entry for creating our in-memory state container is very small.

Note: I am using the standard Blazor template that is generated when creating any new Blazor project.

A quick overview of what we will be implementing:

– Events we will publish

– A composition root for our events

– A way for components to subscribe and un-subscribe from events

First, an interface to describe an event:

“`csharp

public interface IAction

{

}

“`

We are using a blank interface to constrain which objects are events.

We now need to define a few events:

“`csharp

public class DecreaseCounter : IAction

{

}

“`

and…

“`csharp

public class IncreaseCounter : IAction

{

}

“`

These two classes will represent a type of event that we wish to propagate throughout the entire application. We now need to add a composition root that will be the source for publishing our events.

“`csharp

public class StateStore

{

    private readonly Dictionary<Type, Action<IAction>> _actions;

    public StateStore()

    {

        _actions = new Dictionary<Type, Action<IAction>>

        {

            { typeof(IncreaseCounter), action => IncreaseCounterHandler?.Invoke(this, action as IncreaseCounter) },

            { typeof(DecreaseCounter), action => DecreaseCounterHandler?.Invoke(this, action as DecreaseCounter) },

        };

    }

    public void Publish(IAction action)

    {

        _actions[action.GetType()].Invoke(action);

    }

    public event EventHandler<IncreaseCounter> IncreaseCounterHandler;

    public event EventHandler<DecreaseCounter> DecreaseCounterHandler;

}

“`

Our state store has the following properties and methods:

– A Dictionary that acts as a switch-case that will trigger an event handler depending on which event was published

– A “Publish” method that our components will use to publish an event

– Numerous events that components may choose to subscribe to

We need to ensure that any component in our application can have access to our state store. To enable this, we will pass the state store down throughout the entire application as a cascading parameter.

In our App.razor file:

“`csharp

<Router AppAssembly=”@typeof(Program).Assembly” PreferExactMatches=”@true”>

    <Found Context=”routeData”>

        <CascadingValue Value=”Store”>

            <RouteView RouteData=”@routeData” DefaultLayout=”@typeof(MainLayout)” />

        </CascadingValue>

    </Found>

    <NotFound>

        <LayoutView Layout=”@typeof(MainLayout)”>

            <p>Sorry, there’s nothing at this address.</p>

        </LayoutView>

    </NotFound>

</Router>

@code

{

    public StateStore Store { get; set; } = new StateStore();

}

“`

We added a “CascadingValue” that wraps our MainLayout component. In the code section, we initialized our object. Any component may now publish or subscribe to events.

The default counter component contains a button that (when clicked) keeps track of the click counts internally. We will modify the component to publish events to our state store instead. We will then create a new component that listens and reacts accordingly.

“`csharp

@page “/counter”

@using StateManagement.State

@using StateManagement.State.Events

<h1>Counter</h1>

<button class=”btn btn-primary” @onclick=”() => Store.Publish(new IncreaseCounter())”>Increase Counter</button>

<button class=”btn btn-primary” @onclick=”() => Store.Publish(new DecreaseCounter())”>Decrease Counter</button>

@code {

    [CascadingParameter]

    public StateStore Store { get; set; }

}

“`

Our component now accepts the state store as a cascading parameter which we will use to publish counter events. We have two buttons that will publish an event to either increase or decrease the counter.

We now need a component to react to the events that are being published:

“`csharp

@using StateManagement.State

@using StateManagement.State.Events

@implements IDisposable

<p>Current count: @CurrentCount</p>

@code {

    [CascadingParameter]

    public StateStore Store { get; set; }

    public int CurrentCount { get; set; }

    protected override void OnInitialized()

    {

        Store.IncreaseCounterHandler += IncreaseCount;

        Store.DecreaseCounterHandler += DecreaseCount;

        base.OnInitialized();

    }

    private void IncreaseCount(object? sender, IncreaseCounter e)

    {

        ++CurrentCount;

        StateHasChanged();

    }

    private void DecreaseCount(object? sender, DecreaseCounter e)

    {

        –CurrentCount;

        StateHasChanged();

    }

    public void Dispose()

    {

        Store.IncreaseCounterHandler -= IncreaseCount;

        Store.DecreaseCounterHandler -= DecreaseCount;

    }

}

“`

There is a lot happening in this component, summarized by the following:

  • Our counter subscriber component accepts the state store as a cascading parameter just like the counter component
  • We declare a variable for the “CurrentCount” that will be displayed in the UI
  • We override the “OnInitialized” method to subscribe to the events
  • We implement a method for each event that updates the “CurrentCount” property and calls the “StateHasChanged” method to notify the component to re-render
  • We implement the “IDisposable” interface to un-subscribe from the events

I updated the “index.razor” component to display both components for simplicity.

“`csharp

@page “/”

<h1>Hello, world!</h1>

Welcome to your new app.

<Counter/>

<CounterSubscriber/>

“`

When we click the buttons in our Counter component, the CounterSubscriber should respond:

![Result](https://media.giphy.com/media/ryVEBNJJP4wEplqRVZ/giphy.gif)

This part one was concerned with getting our state store up and running. One of the drawbacks with the current implementation is that we must subscribe to each event, leading to tedious boilerplate code. In part two, we will refactor our state store to contain the state in a central location in order to reduce the code in our subscribers.

Making Digital Practical with Saxony Partners

Technology and digital strategy can be complicated, but our goal at Saxony Partners is simple: Make Digital Practical.

Saxony Partners will meet you where you are and help you get where you want to go. Our pragmatic approach helps ensure early success by leveraging proven technologies and practical solutions in a cost-effective way.

Our team of app dev experts, full-stack developers, and software engineers work with clients to optimize their technology investments and leverage that technology to meet and solve business needs and challenges.

If you or your company is looking for help with software development, reach out to us here.