Based on your description, an "AppState" approach could be useful if I understood it correctly. It's a way to enable communication between components at any level in their hierarchy.
Let's start by defining the AppState
. This class uses the .NET events to raise a notification when a value has changed.
public class AppState
{
private Boolean _loadingIsFinished = false;
public Boolean LoadingIsFinished
{
get => _loadingIsFinished;
set
{
_loadingIsFinished = value;
AppStateChanged?.Invoke(this, EventArgs.Empty);
}
}
public event EventHandler AppStateChanged;
}
The AppState is added to the dependency injection system. For Blazor WASM, it should be a singleton dependency. For Blazor Server, though, it should be a scoped one.
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
...
builder.Services.AddSingleton(new AppState());
...
await builder.Build().RunAsync();
}
The Footer
component uses this state and subscribe to the change event and update a local field. This field is used to determine if the footer is displayed or not. As a good practice, the Footer
implements the IDisposable
to unsubscribe from the event.
@inject AppState _appState
@implements IDisposable
@if (_showFooter == true)
{
<div class="footer">
@*Footer Content*@
</div>
}
@code {
private Boolean _showFooter = false;
protected override void OnInitialized()
{
base.OnInitialized();
_appState.AppStateChanged += AppStateChanged;
_showFooter = _appState.LoadingIsFinished;
}
private async void AppStateChanged(object sender, EventArgs args)
{
_showFooter = _appState.LoadingIsFinished;
await InvokeAsync(StateHasChanged);
}
public void Dispose()
{
_appState.AppStateChanged -= AppStateChanged;
}
}
Because the update of the AppState can happen out of band, we force the UI to update by calling StateHasChanged
. Besides, the call is wrapped inside InvokeAsync
to make sure it is handled correctly by the UI.
In every other component, you can inject the AppState
and change the value of LoadingIsFinished
. Assuming you have a Dashboard
component, you could use the AppState like this.
@inject AppState _appstate
<h3>Dashboard</h3>
@code {
protected override void OnInitialized()
{
base.OnInitialized();
_appstate.LoadingIsFinished = true;
}
}
AppState coupled to authentication
If your question is more about authentication, there is a specialized way to do it. You can inject AuthenticationStateProvider
into the AppState
, introduce a new property like IsAuthenticated
and use this property in your Footer
component.
public AppState(AuthenticationStateProvider authStateProvider)
{
_authStateProvider = authStateProvider;
_authStateProvider.AuthenticationStateChanged += HandleAuthStateChanged;
}
private async void HandleAuthStateChanged(Task<AuthenticationState> task)
{
var state = await task;
IsAuthentication = state.User != null && state.User.Claims.Any();
}
public void Dispose()
{
_authStateProvider.AuthenticationStateChanged -= HandleAuthStateChanged;
}
public event EventHandler AppStateChanged;
private Boolean _isAuthenticated = false;
public Boolean IsAuthenticated
{
get => _isAuthenticated;
set
{
_isAuthenticated = value;
AppStateChanged?.Invoke(this, EventArgs.Empty);
}
}
This is a fundamental version of an "AppState". However, there are frameworks like Fluxor for all these state transitions, or you could use a more decoupled way like a message bus for components, called ComponentBus.