页面刷新或直接链接时,最外面的CascadingValue丢失了

时间:2020-09-17 18:26:50

标签: blazor blazor-server-side syncfusion

我想在Blazor应用程序中共享几个组件。这些碰巧是SyncFusion组件-一个是SfToast,一个是SfDialog。我认为一种简单的方法是将组件放在MainLayout.razor上,然后对每个组件使用<CascadingValue>来将引用传递给所有子页面和组件。

只要通过<NavLink>元素或使用NavigationManager.NavigateTo()进行页面导航,此方法就可以正常工作。但是,如果页面进行了深度链接或刷新,则最外面的<CascadingValue>将变为空。要解决此问题,我创建了一个额外的伪<CascadingValue>作为最外面的伪对象,以确保我真正关心的值填充在刷新或直接链接上,但这感觉像是被黑客入侵了。我想知道我执行此操作的方式是否固有错误,从而导致最外面的<CascadingValue>在刷新时变为空。

下面是一些示例代码来说明此问题。这是非常人为设计的方法,但这是我找出创建显示问题的minimal reproducible example的唯一方法。

如果您运行该项目并单击“转到示例页面”按钮,则会看到CompOne和CompTwo组件引用均已按预期设置为值。但是,如果您随后刷新页面,则会看到CompOne引用(最外面的<CascadingValue>)现在为空。

项目的布局如下(从默认的Blazor Server模板创建,因此我仅显示了进行修改的区域)

+ 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; }
}

2 个答案:

答案 0 :(得分:4)

原因

就像引用的HTML元素一样,直到渲染后,引用的组件也不会链接。这是因为该组件是作为BuildRenderTree的一部分创建的,此时引用已链接起来,就像这样。

<SurveyPrompt @ref=MySurveyPrompt Title="How is Blazor working for you?" />

@code

{
    SurveyPrompt MySurveyPrompt;
}

将转换为followint C#(在obj\Debug\net5.0\Razor\Pages中查看)

__builder.OpenComponent<BlazorApp58.Shared.SurveyPrompt>(1);
__builder.AddAttribute(2, "Title", "How is Blazor working for you?");

// This is the line that captures the reference
__builder.AddComponentReferenceCapture(3, (__value) => {
      MySurveyPrompt = (BlazorApp58.Shared.SurveyPrompt)__value;
  });
  __builder.CloseComponent();

因此,第一次渲染(即直接导航)时,在渲染期间(即CascadingValue渲染时)没有引用-而是单击按钮或引起{{1 }} Blazor将自动重新渲染以检查更改。至此,您已经具有要渲染的引用。

解决方案

  • 首先,在布局中添加字段或属性NavigationManager.NavigateTo
  • 然后,在用户界面中,确保第一个渲染(在您具有引用之前)不呈现您要引用的组件以外的任何子内容。
bool HasRendered
  • 然后,在第一个渲染固定您的组件引用之后,允许完整渲染使用者标记并告诉Blazor重新渲染。

@if (HasRendered) { <CascadingValue Value=@ComponentOne> <CascadingValue Value=@ComponentTwo> ... your markup here ... </CascadingValue> </CascadingValue> } <ComponentOne ...../> <ComponentTwo ...../> 中执行以下操作

OnAfterPaint

其他

由于在任何使用者使用它们时CascadingValue组件的值不会改变,因此您还可以添加if (firstRender) { HasRendered = true; StateHasChanged(); } ,这将改善渲染性能。

答案 1 :(得分:0)

级联参数的执行是从下向下执行的,页面刷新不考虑父组件实例,因此它为null。检查以下链接以供参考。

父实例:Blazor component : refresh parent when model is updated from child component