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 two 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 2

In the first part of our series, we created a state store which allowed us to publish and subscribe to events in our application. This allowed us to have a separate component that listened to events published from our counter component.

The biggest drawback to our current implementation is the amount of boilerplate in our subscription components. They must implement at least three methods and an additional method per event to which they are subscribed. Another issue is that every subscription component that responds to events needs to keep its own version of what the current state may be. An easier and simpler implementation would be for the state store to hold a single copy of the applications’ state the entire time. Each subscribing component would listen to a single event indicating all subscribers to re-render.

We need to introduce an object to centralize our application’s state:

“`csharp

public class InitialState

{

    public int Count { get; set; }

}

“`

Our count property is now located in a single location, enabling us to centralize our application’s state to one location and reduce the number of events handlers.

We will use a single event to notify subscribers that the application state has been updated:

“`csharp

public class ApplicationStateChanged

{

}

“`

We can now update our state store to handle our count property internally and publish our new event.

“`csharp

public class StateStore

{

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

    public InitialState State { get; }

    public StateStore()

    {

        State = new InitialState();

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

        {

            { typeof(IncreaseCounter), action => UpdateState(state => ++state.Count) },

            { typeof(DecreaseCounter), action => UpdateState(state => –state.Count) },

        };

    }

    public void Publish(IAction action)

    {

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

    }

    private void UpdateState(Action<InitialState> stateAction)

    {

        stateAction.Invoke(State);

        ApplicationStateChangedHandler?.Invoke(this, new ApplicationStateChanged());

    }

    public event EventHandler<ApplicationStateChanged> ApplicationStateChangedHandler;

}

“`

Our new state store has been simplified with only one event. In other words, the entire state of our application is contained in a single object. When we publish an event to our state store, the state will be updated, and an “ApplicationStateChanged” event will be published.

Our component that subscribes to our events can now be a lot simpler:

“`csharp

@using StateManagement.State

@implements IDisposable

<p>Current count: @Store.State.Count</p>

@code {

    [CascadingParameter]

    public StateStore Store { get; set; }

    protected override void OnInitialized()

    {

        Store.ApplicationStateChangedHandler += ReRender;

        base.OnInitialized();

    }

    private void ReRender(object sender, ApplicationStateChanged e) => StateHasChanged();

    public void Dispose() => Store.ApplicationStateChangedHandler -= ReRender;

}

“`

We have removed the majority of the boilerplate code from our subscribing component. We have also removed our local reference to the current count.

While this implementation is a major upgrade over the previous one, we can improve it further. Since our subscribing components need only to listen to a single event, we consequently have a common interface. Whenever we have a common interface in our code, we can extract it into an interface or base class. In part three of this series, we will extract the remainder of the subscriber code into a base class that will allow us to continue the simplification process.

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.