I have a couple components that I want to share throughout my Blazor app. These happen to be SyncFusion components - one is an SfToast and one is an SfDialog. I thought an easy way to do this would be to put the components on MainLayout.razor and then use a <CascadingValue> for each to pass the reference along to all child pages and components.
This works fine as long as navigation to a page occurs via a <NavLink> element or using NavigationManager.NavigateTo(). However, if a page is deep linked or refreshed, the outermost <CascadingValue> becomes null. To work around this I have created an additional dummy <CascadingValue> as the outermost which ensures that the values I truly care about are populated on a refresh or direct link, but this feels like a hack. I'd like to know if there is something inherently wrong with the way I am doing this that is causing the outermost <CascadingValue> to become null on a refresh.
Below is some sample code to illustrate the problem. This is very contrived, but it's the only way I could figure out to create a minimal reproducible example that shows the issue.
If you run the project and click on the "Go to Sample Page" button, you will see that both CompOne and CompTwo component references are set to values, as expected. However, if you then refresh the page you will see that the CompOne reference (the outermost <CascadingValue>) is now null.
The layout of the project is as follows (created from the default Blazor Server template so I've only shown the areas where I made modifications):
+ BlazorSample | + Components (I added this folder) | - ComponentOne.razor | - ComponentTwo.razor | + Pages | - Index.razor | - SamplePage.razor | + Shared | - MainLayout.razor
MainLayout.razor
@inherits LayoutComponentBase
<div class="sidebar">
<NavMenu />
</div>
<div class="main">
<div class="top-row px-4">
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
</div>
<div class="content px-4">
<CascadingValue Name="CompOne" Value="ComponentOne">
<CascadingValue Name="CompTwo" Value="ComponentTwo">
@Body
</CascadingValue>
</CascadingValue>
</div>
<BlazorSample.Components.ComponentOne @ref="ComponentOne"></BlazorSample.Components.ComponentOne>
<BlazorSample.Components.ComponentTwo @ref="ComponentTwo"></BlazorSample.Components.ComponentTwo>
</div>
@code{
public BlazorSample.Components.ComponentOne ComponentOne;
public BlazorSample.Components.ComponentTwo ComponentTwo;
}
ComponentOne.razor
@code {
// this would normally contain something useful
public string ThisIsNotUseful = "This is just a sample";
}
ComponentTwo.razor
@code {
// this would normally contain something useful
public string ThisIsNotUsefulEither = "This is just a sample";
}
Index.razor
@page "/"
@inject NavigationManager NavigationManager
<button class="btn btn-primary"
@onclick="@(() => NavigationManager.NavigateTo("/samplepage"))">
Go to Sample Page
</button>
SamplePage.razor
@page "/samplepage"
<div class="row">
<div class="col-12">
CompOne is null: @(CompOne == null)
</div>
</div>
<div class="row">
<div class="col-12">
CompTwo is null: @(CompTwo == null)
</div>
</div>
@code{
[CascadingParameter(Name = "CompOne")]
public BlazorSample.Components.ComponentOne CompOne { get; set; }
[CascadingParameter(Name = "CompTwo")]
public BlazorSample.Components.ComponentTwo CompTwo { get; set; }
}
NavigationManager.NavigateTo("/samplepage")- only on a refresh or a direct link. Even then it's only the outermost<CascadingValue>that is null. I guess my solution of wrapping another<CascadingValue>around the outside is the only fix then? - Lex