Quantcast
Channel: Active questions tagged blazor - Stack Overflow
Viewing all articles
Browse latest Browse all 4839

ASP.NET Core Blazor -> Scoped AppState lifecycle of Blazor components

$
0
0

I am using ASP.NET Core to build a simple login with multiple Blazor components.

I want to store my AppState such as IsLoggedIn inside my AppState code and subscribe to the OnChange method for UI updates.

namespace PulseVenture.PhotoGallery.Components.Services.State{    public class AppState    {        public Guid InstanceId { get; } = Guid.NewGuid();        public AppState()        {            Console.WriteLine($"AppState instance created at {DateTime.Now} with ID: {InstanceId}");        }        public bool IsLoggedIn { get; private set; }        public string? UserName { get; private set; }        public event Action? OnChange;        // LogIn Methode needs Validation @ToDo        public void LogIn(string? userName)        {            IsLoggedIn = true;            UserName = userName;            NotifyStateChanged();        }        public void LogOut()        {            IsLoggedIn = false;            UserName = null;            NotifyStateChanged();        }        // Invoke, updating the UI or performing other actions        //private void NotifyStateChanged() => OnChange?.Invoke();        private void NotifyStateChanged()        {            Console.WriteLine($"NotifyStateChanged invoked. OnChange is {(OnChange == null ? "null" : "not null")}. with Instance: {InstanceId}");            if (OnChange == null)            {                Console.WriteLine($"Warning: OnChange is null when NotifyStateChanged is called. with Instance: {InstanceId}");            }            OnChange?.Invoke();        }    }}

Inside my components such as the NavMenu I am injecting the AppState.

@using Components.Services.State@implements IDisposable@inject AppState AppState...@if (AppState.IsLoggedIn){<Layout.NavComponents.UploadButton />}else{<Layout.NavComponents.LogInButton  />}...@code {    //SingOut Logic    private void LogOut() => AppState.LogOut();    // subscribe to the OnChange event and trigger a re-render with StateHasChanged    protected override void OnInitialized()    {        Console.WriteLine($"Component initialized with AppState instance: {AppState.InstanceId}");        if (AppState != null)        {            AppState.OnChange += () =>                    {                        Console.WriteLine($"State changed detected in component. with Instance: {AppState.InstanceId}");                        StateHasChanged();                    };            Console.WriteLine($"Subscribed to AppState.OnChange with Instance: {AppState.InstanceId}");        }        else        {            Console.WriteLine("AppState is null in OnInitialized");        }    }    public void Dispose()    {        if (AppState != null)        {            Console.WriteLine($"Disposed instance: {AppState.InstanceId}");            AppState.OnChange -= StateHasChanged;        }    }}

And inside my LogInButton is a child component named LogInPopUp which also uses the AppState:

@rendermode InteractiveServer@using Components.Services.State@implements IDisposable@inject AppState AppState<div class="popup-backdrop" style="@(Show ? "display:block;" : "display:none;")"><div class="card popup-content"><button class="exit-btn" @onclick="ClosePopup"><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z" /></svg></button><span class="card_title">Log In</span><p class="card_content">Upload Documents to share straight to your cliends.</p><div class="card_form"><input placeholder="Your Email" type="text" @bind="UserEmail"><button class="sign-in" @onclick="LogIn"> Sign In</button></div><p class="signup-link">      No Account?<a href="">Request Access</a></p></div></div>@code {    [Parameter]    public bool Show { get; set; }    [Parameter]    public EventCallback<bool> ShowChanged { get; set; }    private async Task ClosePopup()    {        Show = false;        if (ShowChanged.HasDelegate)        {            await ShowChanged.InvokeAsync(Show);        }    }    // LogIn Logic    private string UserEmail = string.Empty;    private void LogIn()    {        AppState.LogIn(UserEmail);        ClosePopup();    }    protected override void OnInitialized()    {        Console.WriteLine($"Component initialized with AppState instance: {AppState.InstanceId}");        if (AppState != null)        {            AppState.OnChange += () =>    {        Console.WriteLine($"State changed detected in component. with Instance: {AppState.InstanceId}");        StateHasChanged();    };            Console.WriteLine($"Subscribed to AppState.OnChange with Instance: {AppState.InstanceId}");        }        else        {            Console.WriteLine("AppState is null in OnInitialized");        }    }    public void Dispose()    {        if (AppState != null)        {            Console.WriteLine($"Disposed instance: {AppState.InstanceId}");            AppState.OnChange -= StateHasChanged;        }    }}

But when I run my code, I get the output that both elements subscribe to the appState and when they are done rendering they immediately dispose of the AppState and create a new instance.

Output:

AppState instance created at 21.11.2024 18:17:02 with ID: 872221af-23da-4427-8dc5-c5b5ac0e9f45Component initialized with AppState instance: 872221af-23da-4427-8dc5-c5b5ac0e9f45Subscribed to AppState.OnChange with Instance: 872221af-23da-4427-8dc5-c5b5ac0e9f45Component initialized with AppState instance: 872221af-23da-4427-8dc5-c5b5ac0e9f45Subscribed to AppState.OnChange with Instance: 872221af-23da-4427-8dc5-c5b5ac0e9f45Disposed instance: 872221af-23da-4427-8dc5-c5b5ac0e9f45Disposed instance: 872221af-23da-4427-8dc5-c5b5ac0e9f45

I inject the AppState as a scoped DI.

I already tried using different DI injections but I technically need to use

builder.Services.AddScoped<AppState>();

because my application can have more than one user.

Furthermore I tried passing the AppState down from the parent which also doesn't work.


Viewing all articles
Browse latest Browse all 4839

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>