What is Redux?

Redux is basically a state management tool for JavaScript applications. It is frequently used with React application, it is works with any other JavaScript framework like Angular, others React-like frameworks and virtually any other JavaScript frameworks even vanilla JavaScript. Redux works by storing the state of the application in one central location. Each component of an application can have access to the state of the application without using ‘props’ or using callback function to send data back up to the parent component.

Redux on Blazor

How can Redux be of use in Blazor? Well since Blazor offers Interop, implementing Redux could be possible, in fact there already exist library that implements Redux-style management for Blazor. Today we are going to setup Redux on a Blazor Server-Side project.

Redux Library

We will use an open source Blazor Redux library, we’ll use github user Torhovland Blazor-Redux library. Blazor have had a couple of version updates in the past months this cause the library to be outdated, for the benefits of the blog post I forked the repo(here is my forked repo ) and updated the library to work with the latest version of Blazor(3.0.0-preview7.19365.7). You can download the repo and add the ‘BlazorRedux’ project as a dependency to you Blazor project. After adding it has a dependency you can use it on your project. I will do my best on keeping updated the library to the latest Blazor version, once the owner of the repo accepts all the PR are accepted and update the NuGet package you can just download the package and use it instead of adding the project itself has a dependency.

You came here to learn how to setup Redux on Blazor lets start

First Things First

  1. Make sure you have the latest .NetCore preview, Visual Studio 2019 preview and all the Blazor requirements installed
  2. Create a Blazor Server-Side project
  3. Download my repo and add BlazorRedux has an existing project to the solution

Now that our requirements are met, we can start doing some magic, to keep things simple I will implement Redux in the Counter page and the Weather page. We’ll add a custom class to the Store, this will act as the state of our application, we’ll also add Actions for each property of the Store, we’ll also add a Reducer to pass the Action that we want to execute.

State

This is a custom class that will hold the actual state of our app, is just a custom class with some properties. Below I show you a class named ‘MyState’ that have three properties

namespace BlazorReduxApp.Data.Redux.States
{
    public class MyState
    {
        public int Count { get; set; }
        public IEnumerable<WeatherForecast> Forecasts { get; set; }
        public string Location { get; set; }
    }
}

Those properties will by the components in our application. The properties will depend on what your application does.

Actions

Now that we have our State class with all our properties, we can create a Action, this will be what we which to be done in the property, here are a few examples of Actions in different scenarios

public class IncrementByOneAction : IAction {}

Action can have properties

public class ReceiveWeatherAction : IAction
{
    public IEnumerable<WeatherForecast> Forecasts { get; set; }
}

You can even pass arguments to the constructor

public class IncrementByValueAction : IAction
    {
        public IncrementByValueAction(int value)
        {
            Value = value;
        }
        public int Value { get; set; }
    }

Actions Async

What about async methods? Well in order to handle asynchronous tasks like fetching data from the backend we can use ActionCreators. They can dispatch several actions during the course of a long-running operation. Below I use ActionCreator to implement ‘LoadWeather’ action

public static class ActionCreators
    {
        public static async Task LoadWeather(Dispatcher<IAction> dispatch)
        {
            var Summaries = new[]
                {
                "Freezing",
                "Bracing",
                "Chilly",
                "Cool",
                "Mild",
                "Warm",
                "Balmy",
                "Hot",
                "Sweltering",
                "Scorching"
                };
        dispatch(new ClearWeatherAction());
            var startDate = DateTime.Now;

            var rng = new Random();
            var forecasts =  Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = startDate.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            }).ToArray());

            dispatch(new ReceiveWeatherAction
            {
                Forecasts = forecasts.Result
            });
        }
    }

The recent version of Blazor generates the Weather information at random I just move that logic here.

Reducers

Reducers specify how the application’s state changes in response to actions. Once all the actions are created for the application, we can create a Reducer, below I create a class called ‘Reducers’, to make things simpler I put all my reducers in that class, you can also have a class for each Reducer and create a RootReducer to handle all the reducers. Below is me ‘Reducers’ class that holds all my reducers and my ‘RootReducer’

  public static class Reducers
    {
        public static MyState RootReducer(MyState state, IAction action)
        {
            if (state == null)
                throw new ArgumentNullException(nameof(state));

            return new MyState
            {
                Location = Location.Reducer(state.Location, action),
                Count = CountReducer(state.Count, action),
                Forecasts = ForecastsReducer(state.Forecasts, action)
            };
        }
        public static int CountReducer(int count, IAction action)
        {
            switch (action)
            {
                case IncrementByOneAction _:
                    return count + 1;
                case IncrementByValueAction a:
                    return count + a.Value;
                default:
                    return count;
            }
        }
        private static IEnumerable<WeatherForecast> ForecastsReducer(IEnumerable<WeatherForecast> forecasts,
           IAction action)
        {
            switch (action)
            {
                case ClearWeatherAction _:
                    return null;
                case ReceiveWeatherAction a:
                    return a.Forecasts;
                default:
                    return forecasts;
            }
        }
    }

If you want to move the reducers to their own class file, just create the class and the reducer you want. Keep in mind that if you do that you must import the namespace in the ‘RootReducer’ class for the RootReducer to use them.

We are half way through hang in there!

Blazor Components

BlazorRedux library provides us a ‘ReduxComponent’ is basically a normal class that inherits from ComponentBase that receives your application ‘Sate’ class, Action and it configure the redux library for you in the component. I create a master component that ALL my component will inherits, a name it ‘AppBaseComponent’. Below is the component

public class AppBaseComponent: ReduxComponent<MyState, IAction>
    {
    }

We Are Almost Finish Setting Up Redux

In order to make Blazor use the BlazorRedux library we need to add to the DI container. In the ‘Startup.cs’ file we’ll add our Redux Store, we’ll specify the State we created and our RootReducer:

public void ConfigureServices(IServiceCollection services)
        {
            services.AddRazorPages();
            services.AddServerSideBlazor();
            services.AddSingleton<WeatherForecastService>();
            services.AddReduxStore<MyState, IAction>(new MyState(), Reducers.RootReducer, options =>
            {
                options.GetLocation = state => state.Location;
                
            });
        }

Remember adding the library

using BlazorRedux;

Now we have finish setting up Redux on Blazor, but we are not using it anywhere in our app yet, lets start using it!

Using Redux

Let’s implement Redux on the Counter page first, open ‘Counter.razor’, we’ll import the Actions namespace and inherit from our recently created AppBaseComponent

@using BlazorReduxApp.Data.Redux.Actions;
@inherits BlazorReduxApp.Data.Redux.Component.AppBaseComponent

Now our component can access our Redux State, Reducers and Action.

Dispaching Actions

Since our AppBaseComponent class inherits from ReduxComponent we have access to the State inside our component, we can use it as any other property. If we want to make a change, we call a Action through the dispatch method. Below is a sample where every time the button is click the counter property in the State gets incremented by 1.

@page "/counter"
@using BlazorReduxApp.Data.Redux.Actions;
@inherits BlazorReduxApp.Data.Redux.Component.AppBaseComponent
<h1>Counter</h1>

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

<button class="btn btn-primary" @onclick="@(() => Dispatch(new IncrementByValueAction(1)))">Click me</button>

@code {
   
}

Dispaching ActionsCreators

Now, earlier we put our async calls inside a ActionCreators class, in the ‘FetchData.razor’ we will dispatch the Action in the following way

@page "/fetchdata"
@using BlazorReduxApp.Data
@using BlazorReduxApp.Data.Redux.Actions;
@inherits BlazorReduxApp.Data.Redux.Component.AppBaseComponent
@inject WeatherForecastService ForecastService

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from a service.</p>

@if (State.Forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>Date</th>
                <th>Temp. (C)</th>
                <th>Temp. (F)</th>
                <th>Summary</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var forecast in State.Forecasts)
            {
                <tr>
                    <td>@forecast.Date.ToShortDateString()</td>
                    <td>@forecast.TemperatureC</td>
                    <td>@forecast.TemperatureF</td>
                    <td>@forecast.Summary</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {

protected override async Task OnParametersSetAsync()
{
     await ActionCreators.LoadWeather(Store.Dispatch);
}
}

If you run your application, you will see that you can increment the counter and move between pages and the Value of the counter will not be reset.

Hip Hip Hooray!

Congratulation you have finish setting up and implementing Redux in your Blazor application. Now that you got the based setup you can start adding properties, actions and reducers to manage your app state.