Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get a [CascadingParameter] in the second call to OnInitializedAsync()?

When Blazor (.NET 8) is set to:

<Routes @rendermode="@RenderMode.InteractiveServer"/> 

It calls OnInitializedAsync() twice. On the second call, all of the [CascadingParameter] are null. I need these parameters.

How can I get these parameters in the 2nd call? Should I save them off in the first call to then use in the second? Is there a setting that will cause them to retain their values?

And out of curiosity, why does Blazor set them, and then null them?

Answer: (I wish SO allowed selecting multiple answers.) The answers by MrC and Ruikai below are both answers I "accept" and speak to different parts of this question. Please consider both answers selected.

like image 620
David Thielen Avatar asked Nov 02 '25 19:11

David Thielen


2 Answers

are you aware of an example of how to implement an AddLibraryCascadingParameters()

In fact, the AddCascadingAuthenticationState() method in .net 8 is a good example.

In .net 7,when we want to expose the authentication state as a cascading parameter:

[CascadingParameter]
private Task<AuthenticationState>? authenticationState { get; set; }

We have to call

<CascadingAuthenticationState>
    <Router ...>
        <Found ...>
            <AuthorizeRouteView RouteData="@routeData" 
                DefaultLayout="@typeof(MainLayout)" />
            ...
        </Found>
    </Router>
</CascadingAuthenticationState>

Now in .Net 8 ,we just need:

builder.Services.AddCascadingAuthenticationState();

When we check the source codes inside the method:

public static class CascadingAuthenticationStateServiceCollectionExtensions
{
    /// <summary>
    /// Adds cascading authentication state to the <paramref name="serviceCollection"/>. This is equivalent to
    /// having a <see cref="CascadingAuthenticationState"/> component at the root of your component hierarchy.
    /// </summary>
    /// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param>
    /// <returns>The <see cref="IServiceCollection"/>.</returns>
    public static IServiceCollection AddCascadingAuthenticationState(this IServiceCollection serviceCollection)
    {
        return serviceCollection.AddCascadingValue<Task<AuthenticationState>>(services =>
        {
            var authenticationStateProvider = services.GetRequiredService<AuthenticationStateProvider>();
            return new AuthenticationStateCascadingValueSource(authenticationStateProvider);
        });
    }

    private sealed class AuthenticationStateCascadingValueSource : CascadingValueSource<Task<AuthenticationState>>, IDisposable
    {
        // This is intended to produce identical behavior to having a <CascadingAuthenticationStateProvider>
        // wrapped around the root component.

        private readonly AuthenticationStateProvider _authenticationStateProvider;

        public AuthenticationStateCascadingValueSource(AuthenticationStateProvider authenticationStateProvider)
            : base(authenticationStateProvider.GetAuthenticationStateAsync, isFixed: false)
        {
            _authenticationStateProvider = authenticationStateProvider;
            _authenticationStateProvider.AuthenticationStateChanged += HandleAuthenticationStateChanged;
        }

        private void HandleAuthenticationStateChanged(Task<AuthenticationState> newAuthStateTask)
        {
            // It's OK to discard the task because this only represents the duration of the dispatch to sync context.
            // It handles any exceptions internally by dispatching them to the renderer within the context of whichever
            // component threw when receiving the update. This is the same as how a CascadingValue doesn't get notified
            // about exceptions that happen inside the recipients of value notifications.
            _ = NotifyChangedAsync(newAuthStateTask);
        }

        public void Dispose()
        {
            _authenticationStateProvider.AuthenticationStateChanged -= HandleAuthenticationStateChanged;
        }
    }
}
like image 168
Ruikai Feng Avatar answered Nov 04 '25 13:11

Ruikai Feng


I'm not sure what you are doing, but if you are running in Global Interactive Server mode (the equivalent to Net7- Blazor Server), there is no second call except on the initial page load of the SPA.

If you're double rendering every page, you must be returning to the server for every route navigation and therefore not running in full global mode.

Why the cascading values are null?

Probably because the components setting the cascade values are being rendered in a different context to the components consuming them.

Here's my Demo, and a link to the Repo at the bottom. It logs to the console whenever OnInitializedAsync is run and shows the cascaded value.

Blazor Web App template - ServerInteractive and Global options.

Home

@page "/"

<PageTitle>Home</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.

@code {
    private Guid _componentUid = Guid.NewGuid();
    [CascadingParameter(Name = "AppName")] private string AppName { get; set; } = "Not Set";
    protected override Task OnInitializedAsync()
    {
        Console.WriteLine($"{this.GetType().Name} with Id:{_componentUid} ran OnInitializedAsync");
        Console.WriteLine($"{this.GetType().Name} Cascaded Value: {this.AppName}.");
        return base.OnInitializedAsync();
    }
}

I've added the same code to Counter and Weather.

Program:

// Add services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

builder.Services.AddCascadingValue("AppName", sp => (string)"Blazor App");
var app = builder.Build();

And the Console Log:

==>>> Initial Pre-Render 
Home with Id:916bc306-8a3e-48d7-8927-18a63a4fa082 ran OnInitializedAsync
Home Cascaded Value: Blazor App.
==>>> SPA Render
Home with Id:5bb0e524-69a2-439f-8948-f436fe712f60 ran OnInitializedAsync
Home Cascaded Value: Blazor App.
==>>> switching between pages in SPA after here.
Counter with Id:6ef5d724-8f2e-4ed3-8ad3-ad8f09068331 ran OnInitializedAsync
Counter Cascaded Value: Blazor App.
Weather with Id:f7ac9237-a958-40b0-aa3b-09802a768cfa ran OnInitializedAsync
Weather Cascaded Value: Blazor App.
Counter with Id:db668342-28a2-4b2b-9979-eb5dc90519e3 ran OnInitializedAsync
Counter Cascaded Value: Blazor App.
Home with Id:d75040ba-de0b-4fda-9941-fd2d96be5da3 ran OnInitializedAsync
Home Cascaded Value: Blazor App.
Counter with Id:9be0ee88-4184-445f-bf96-550c6066b35c ran OnInitializedAsync
Counter Cascaded Value: Blazor App.
Weather with Id:e9e9da6d-7d96-4442-bb7e-e67793ab4d9d ran OnInitializedAsync
Weather Cascaded Value: Blazor App.
Home with Id:ddfbec45-8d61-4bcc-aa87-2fa1f45d5b06 ran OnInitializedAsync
Home Cascaded Value: Blazor App.

My Demo Repo - https://github.com/ShaunCurtis/SO77787526.

like image 25
MrC aka Shaun Curtis Avatar answered Nov 04 '25 13:11

MrC aka Shaun Curtis