Skip to content

Instantly share code, notes, and snippets.

@Cryptoc1
Last active July 13, 2024 15:54
Show Gist options
  • Save Cryptoc1/f5acc617d204fd214dc498b6515b90e3 to your computer and use it in GitHub Desktop.
Save Cryptoc1/f5acc617d204fd214dc498b6515b90e3 to your computer and use it in GitHub Desktop.
Record Based `Stateful` Blazor Component
@inherits Stateful<SampleState>
@if( !State.IsLoaded )
{
<span>Loading...</span>
}
else
{
<span>Loaded!</span>
}
@code {
protected override void OnInitialized()
{
if( !TryRestoreFromPersistence( "sample" ) )
{
Mutate(SampleState.Load);
}
}
}
public sealed record SampleState : State
{
public bool IsLoaded { get; init; }
public static SampleState Load( SampleState state )
=> state with { IsLoaded = true };
}
public abstract record State;
public abstract class Stateful<[DynamicallyAccessedMembers( DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties )] T> : ComponentBase, IDisposable
where T : State, new()
{
private bool disposed;
private PersistingComponentStateSubscription? persistence;
[Inject]
private PersistentComponentState PersistentState { get; init; } = default!;
protected T State { get; private set; } = new();
public void Dispose( )
{
Dispose( true );
GC.SuppressFinalize( this );
}
protected virtual void Dispose( bool disposing )
{
if( !disposed )
{
if( disposing )
{
persistence?.Dispose();
}
disposed = true;
}
}
protected async Task Mutate( Func<T, Task<T>> mutator )
{
var state = await mutator( State );
if( State != state )
{
State = state;
StateHasChanged();
}
}
protected void Mutate( Func<T, T> mutator )
{
var state = mutator( State );
if( State != state )
{
State = state;
StateHasChanged();
}
}
[UnconditionalSuppressMessage( "Trimming", "IL2026", Justification = "The generic type parameter 'T' is properly annotated to prevent trimming of metadata required by serialization." )]
protected bool TryRestoreFromPersistence( string key )
{
ArgumentException.ThrowIfNullOrWhiteSpace( key );
if( PersistentState.TryTakeFromJson<T>( key, out var state ) )
{
State = state ?? new();
return true;
}
persistence ??= PersistentState.RegisterOnPersisting( ( ) =>
{
PersistentState.PersistAsJson( key, State );
return Task.CompletedTask;
} );
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment