3
votes

So I have a Blazor application in which a user logs in and the header component changes depending on whether a user is logged in or not. After the use logs in, they are redirected to the main home page, but the header component does not update unless I hit the refresh button on the browser. I tried using StateHasChanged(). Here is my relevant code in the header component:

@using Newtonsoft.Json
@inject IJSRuntime JsRuntime
@inject NavigationManager NavManager

@if (!string.IsNullOrWhiteSpace(FirstName))
{
    <div class="header-user-widget">
        <button class="btn btn-cart">
            Cart <span class="badge badge-cart">@CartCount</span>
        </button>
        <i class="fa fa-user-circle"></i> @FirstName @LastName
        <i class="fa fa-sign-out-alt header-sign-out" @onclick="SignOutClicked"></i>
    </div>
}

@code {
    private string FirstName { get; set; }
    private string LastName { get; set; }
    private int CartCount { get; set; }

    protected override async Task OnInitializedAsync()
    {
        var page = NavManager.ToBaseRelativePath(NavManager.Uri).ToLower();
        var cookie = await JsRuntime.InvokeAsync<string>("Cookies.get", "Login");

        if (!page.StartsWith("login"))
        {
            if (string.IsNullOrWhiteSpace(cookie))
            {
                NavManager.NavigateTo("/Login");
                return;
            }
        }
        if (!string.IsNullOrWhiteSpace(cookie))
        {
            var decodedCookie = cookie.FromBase64String();

            FirstName = CookieHelper.GetValueFromKey(decodedCookie, "FirstName");
            LastName = CookieHelper.GetValueFromKey(decodedCookie, "LastName");
        }

        CartCount = await NumberOfItemsInCart();
    }
}

And here is my login page:

@page "/login"

@inject IJSRuntime JsRuntime
@inject NavigationManager NavManager

<LoginBox OnLoginButtonClick="@LoginButtonClicked" />

@code {

    private async Task LoginButtonClicked(LoginBoxReturnModel model)
    {
        var cookieString = $"UserId={model.UserId}|FirstName={model.FirstName}|LastName={model.LastName}|Email={model.EmailAddress}|IsAdmin={model.IsAdmin}";
        var encyptedString = cookieString.ToEncryptedBase64String();

        await JsRuntime.InvokeVoidAsync("Cookies.set", "Login", encyptedString);

        StateHasChanged(); // This line doesn't seem to have any effect
        NavManager.NavigateTo("/");
    }
}

Once the user logs in, they are properly redirected to "/" page, but the header is not updated. If I hit F5 in the browser to refresh it, then the header is correct. I feel like what is going on is that the application is navigating before the StateHasChanged() line has any time to refresh, thus it is never refreshing. If this is the case, how should I be implementing this?

1
Where's the rest of the header and the page it appears in? Blazor is essentially React# which means components react/refresh in response to changes to their properties. What you posted shows that data is only loaded when the component is initialized the first time, loading data from cookies and unspecified sources. There are no component properties though, or something that actually changes the dataPanagiotis Kanavos
@PanagiotisKanavos - The header component is being used in my main layout page. I can post it, if you want, but it's just a standard layout page with my <Header /> tag in it. I understand that no component properties are being changed. That is why I am calling StateHasChanged() because my understanding is that calling that method forces components to refresh regardless of whether or not a property is changed.Icemanind
On which component? StateHasChanged doesn't mean RefreshAll. That's why I said it's React#. The components refresh only if their state (their parameters) changed. Instead of every component in a page refreshing, only the affected components do. The header you posted doesn't even have parameters, so it can't receive any state from the outside or respond to changesPanagiotis Kanavos
If you want the user and cart count properties to change dynamically, they should be exposed as component parameters that will be modified by the header's parentPanagiotis Kanavos
I misunderstood what StateHasChanged does. I was under the impression that calling that would refresh all the visible components on the page, not just the component it's called from. So you're saying the way to make this work is to create a public method in the component, like Update() or something and then if the header needs to be refreshed, just called that Update() method, which in turn reads the cookie and updates itself accordingly?Icemanind

1 Answers

2
votes

Drop the StateHasChanged(), your navigating from "/login" to "/". The true should force the load of the cookie like f5 is doing.

NavManager.NavigateTo("/",true);