Skip to content

Tailslide/fluxor-persist

Repository files navigation

Fluxor-persist

Fluxor-persist is a library to persist Fluxor states.

Introduction

Persisting states is a pretty common task .. in react I use redux-persist so this carries over some of the same ideas. Most often I use it to ensure a user doesn't lose their state on a page refresh or when leaving or returning to a site but this library has no blazor dependencies so it can be used anywhere.

Getting Started

Installation

You can download the latest release / pre-release NuGet packages from the official Fluxor-persist nuget pages.

Setup

The easiest way to get started is to look at the sample blazor app here(https://github.com/Tailslide/fluxor-persist)

To add Fluxor-persist to your existing blazor fluxor application you want to:

  • Add a NuGet package reference to Fluxor.Persist
  • Add .UsePersist() in Program.cs when building your existing Fluxor service with AddFluxor()
  • Make a class that implements IStringStateStorage to persist your state however you want. It just needs to be able to save and retrieve a string / string key value pair. You can alternative implement IObjectStateStorage if you need to persist using the state objects directly.
  • Add the following to your Program.cs to register both your state storage and the default persists json store handler:
builder.Services.AddScoped<IStringStateStorage, LocalStateStorage>();
builder.Services.AddScoped<IStoreHandler, JsonStoreHandler>();

Detecting Rehydrate

You can detect that state has been rehydrated from storage. I use this in my MainLayout which inherits from FluxorLayout. In OnInitialized after I intialize the middleware to detect state restore and force a refresh:

        this.SubscribeToAction<InitializePersistMiddlewareResultSuccessAction>(result =>
        {
            Console.WriteLine($"**** State rehydrated**");
            this.StateHasChanged();// we now have state, we can re-render to reflect binding changes
        });

Example State Storage Class for LocalStorage

using Blazored.LocalStorage;
using Fluxor.Persist.Storage;
using System.Threading.Tasks;

namespace Fluxor.Persist.Sample.Storage
{
    public class LocalStateStorage :IStringStateStorage
    {

        private ILocalStorageService LocalStorage { get; set; }

        public LocalStateStorage(ILocalStorageService localStorage)
        {
            LocalStorage = localStorage;
        }

        public async ValueTask<string> GetStateJsonAsync(string statename)
        {
            return await LocalStorage.GetItemAsStringAsync(statename);
        }

        public async ValueTask StoreStateJsonAsync(string statename, string json)
        {
            await LocalStorage.SetItemAsStringAsync(statename, json);
        }
    }
}

Advanced Usage - BlackList, WhiteList

You can blacklist or whitelist state names to indicate if they should be persisted. Use only a blacklist or a whitelist not both. Regardless of settings, the states @routing and PersistMiddleware are never persisted.

Examples:

.UsePersist(x => x.SetBlackList(new string[] { "mystate1", "mystate2" }))
.UsePersist(options => options.UseInclusionApproach())
.UsePersist(x => x.SetWhiteList(new string[] { "mystate1", "mystate2" }))

Advanced Usage - Persist, SkipPersist

Similarly, you can mark state classes to persit or not with [Persist] and [SkipPersist] attributes. States can be not persisted by default by initializing with .UsePersist(options => options.UseInclusionApproach())

Advanced Usage - Persist only some state properties

You can attribute your state records so that certain fields do not get serialized. To not persist a 'isloading' flag for example:

    public record LoginState (
        [property: JsonIgnore] bool IsLoading,
        int CounterValue
        ) 
    {        
        [JsonConstructor]
        public LoginState(int CounterValue) : this(false, CounterValue) { }
    }

BREAKING CHANGES IN 1.09

To convert from using persists pre-1.09 to 1.09+, you need the following changes:

This line can be removed from MainLayout and is no longer required:

Dispatcher.Dispatch(new InitializePersistMiddlewareAction() { StorageService = new LocalStateStorage(this.localStorage), RehydrateStatesFromStorage=true });

Your storage class that implements IStateStorage needs to change to use IStringStateStorage

The following two lines need to be added to Program.cs to register the default JSON handler and also your local storage class:

builder.Services.AddScoped<IStringStateStorage, LocalStateStorage>();
builder.Services.AddScoped<IStoreHandler, JsonStoreHandler>();

If you have a whitelist or a blacklist the method changed for setting them from:

.UsePersist(x => x.StateBlackList= "mystate1,mystate2")

to

.UsePersist(x => x.SetBlackList(new string[] { "mystate1", "mystate2" }))

BREAKING DEPENDENCY For Sample Project - Blazored.LocalStorage V4.0

Looks like there is a new version of Blazored.Localstorage that has an issue that affects the sample project: Blazored/LocalStorage#140

If you use Blazored.LocalStorage V4.1.1 + you will need to change your LocalStateStorage.cs class to use SetItemAsStringAsync instead of SetItemAsync. You will also need to clear localstorage as your states have been corrupted if you upgraded the nuget without making this change.

I updated the sample app to use this new method.