Fluxor-persist is a library to persist Fluxor states.
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.
You can download the latest release / pre-release NuGet packages from the official Fluxor-persist nuget pages.
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 withAddFluxor()
- 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 implementIObjectStateStorage
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>();
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
});
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);
}
}
}
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" }))
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())
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) { }
}
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" }))
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.