DEV Community

Rob King
Rob King

Posted on

Persisting Fluxor state in Blazor WASM

If you are using Blazor WebAssembly with Fluxor, you will likely have come across the situation where refreshing the current page wipes out your state and sets it back to defaults.

This is not unique to Blazor/Fluxor - it happens in React/Redux too - but it’s a behaviour that is guaranteed to frustrate users.

This is one approach you can take to ensure that a browser refresh doesn't wipe out your Fluxor state.

Prerequisites

The following assumes you already have a Blazor WebAssembly project set up with Fluxor. If not, head over to https://github.com/mrpmorris/Fluxor and get started.

Step 1 - Session storage

First off we need to install Blazored.SessionStorage following the instructions at https://github.com/Blazored/SessionStorage. This gives us the core functionality to read and write objects to the browser’s Session Storage.

builder.Services.AddBlazoredSessionStorageAsSingleton();
Enter fullscreen mode Exit fullscreen mode

Step 2 - Add a new action and reducer

At the heart of Fluxor are actions which mutate state via reducers. The beauty of actions is they can pass any object on their properties.

Normally an action would pass properties specific to a state, but this time we want an action that passes the state itself.

public record LoadUsersFromStorageAction(UsersState storedState);
Enter fullscreen mode Exit fullscreen mode

Now that the action is created, we need to mutate the state in the reducer using the state object on the action.

[ReducerMethod]
        public static UsersState ReduceLoadUsersFromStorageAction(UsersState state, LoadUsersFromStorageAction action)
        {
            return state with
            {
                ErrorMessage = action.storedState.ErrorMessage,
                LoadingStatus = action.storedState.LoadingStatus,
                Users = action.storedState.Users
            };
        }
Enter fullscreen mode Exit fullscreen mode

Step 3 - Add a new wrapper component

Create a new Razor component called something like FluxorSessionStorage.razor that is going to handle the reads and writes e.g.

@inject IDispatcher _dispatcher;
@inject ISyncSessionStorageService _sessionStorage;
@inject IState<UsersState> _usersState;

@ChildContent

@code {
    protected override void OnInitialized()
    {
        // read
        var savedState = _sessionStorage.GetItem<UsersState>("UsersState");
        if (savedState != null)
        {
            _dispatcher.Dispatch(new LoadUsersFromStorageAction(savedState));
        }

        // write
        _usersState.StateChanged += ((state, e) =>
        {
            var data = ((IState<UsersState>)state).Value;
            _sessionStorage.SetItem<UsersState>("UsersState", data);
        });
    }

    [Parameter] public RenderFragment? ChildContent { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

As you can see, the OnInitialized method is used to wire up the storage methods. These methods are only called when the component is loaded on refresh.

The component will wrap the entire Blazor app thereby capturing all the Fluxor activity.

<Fluxor.Blazor.Web.StoreInitializer />
<CascadingAuthenticationState>
    <FluxorSessionStorage>
        <Router AppAssembly="@typeof(App).Assembly">
            <Found Context="routeData">
                <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
                <FocusOnNavigate RouteData="@routeData" Selector="h1" />
            </Found>
            <NotFound>
                <PageTitle>Not found</PageTitle>
                <LayoutView Layout="@typeof(MainLayout)">
                    <p role="alert">Sorry, there's nothing at this address.</p>
                </LayoutView>
            </NotFound>
        </Router>
    </FluxorSessionStorage>
</CascadingAuthenticationState>
Enter fullscreen mode Exit fullscreen mode

Step 4 - Test it

Now when we run the application, we should see our Fluxor state being persisted in the browser's Session Storage.

Image description

If we make any change to the state, we trigger a write to Session Storage.

Image description

Likewise, if we hit F5, we load the state from Session Storage.

Image description

Top comments (2)

Collapse
 
mr_eking profile image
Eric King

Nice.

I'm wondering how well this would work with a large application state.

I have a few applications that end up with a relatively large amount of data in the various Fluxor stores, and I wonder if saving the state to storage on every update would have a noticeable impact on performance. Right now I'm selectively saving a small portion of the state to storage at specific times, but not everything on every update.

Collapse
 
robert_p_king profile image
Rob King

I haven’t tried it with a larger state yet so, yes there may be limitations in this approach.