Search code examples
c#asynchronousblazorcascading

Blazor async cascading value always null


Let's say I have the following code in MainLayout.razor

<CascadingValue Value="@(async () => await someclass.GetValueAsnyc())" Name="asyncValue">
  <article class="content px-4">
   @Body
  </article>
</CascadingValue>

And in Index.razor:

<h1>Hello!</h1>
@code{
     [CascadingParameter(Name = "asyncValue")] public string AsyncValue{ get; set; }


protected override async Task OnInitializedAsync()
{
   //do something with AsyncValue- it is always null
}
}

My assumption is that the declaration of the variable in Index.razor isn't awaiting the cascading value in Mainlayout.razor. It does work is I call the method synchronsly with .Result at the end, but that's not what I would like to do if possible.

Does anyone have any guidance or can point me in the right direction so that the cascading value loads asynchronously?


Solution

  • I think this should help.

    First a consumer component. The cascaded value is a Task which may or may not have completed. To consume it you await it. If it's already completed the there's no awaiting to do. It provides the result immediately.

    <h3>@this.value</h3>
    <button class="btn btn-primary" @onclick=this.OnClick>Get Value Again</button>
    @code {
        private string value = string.Empty;
        [CascadingParameter] private Task<string>? DataTask { get; set; }
    
        protected override async Task OnInitializedAsync()
        {
            if (this.DataTask is not null)
                this.value = await DataTask;
        }
    
        private async Task OnClick()
        {
            if (this.DataTask is not null)
                this.value = await DataTask;
        }
    }
    

    And a test page. I haven't put it in a layout or App as it's easier to see what's happening in a test page. The cascaded value is a Task<string> field that you assign a method to.

    @page "/Test"
    
    <PageTitle>Test</PageTitle>
    
    <CascadingValue Value="this.DataTask">
        <MyComponent />
    </CascadingValue>
    
    @code {
        private Task<string>? DataTask;
    
        protected override Task OnInitializedAsync()
        {
            DataTask = GetSomeDateAsync();
            return Task.CompletedTask;
        }
    
        private async Task<string> GetSomeDateAsync()
        {
            await Task.Delay(5000);
            return DateTime.Now.ToLongTimeString();
        }
    }
    

    This is basically how the AuthenticationState cascade works.