Blazor WASM - 使用 RouteData 离开 Layout 导航创建重复的组件

Blazor WASM - Navigation away from Layout using RouteData creates duplicate components

经过大量努力,我终于找到了我所经历的根本原因。每当将 RouteData 作为 CascadingParameter 注入到 Layout 中,然后导航到不同的 Layout 时,Blazor 将构建两个新组件并在处理其中一个之前初始化它们。因此,在 OnInitializedAsync 期间获取的任何数据都将执行两次,而没有任何检测方法。

有人对此有解决方法吗?我在他们的回购协议中找到了这张旧票,但看起来并不多 activity。 https://github.com/dotnet/aspnetcore/issues/20637

App.razor:

<Router AppAssembly="@typeof(App).Assembly">
    <Found Context="routeData">
        <CascadingValue Value="@routeData">
            <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
        </CascadingValue>
    </Found>
    <NotFound>
        <PageTitle>Not found</PageTitle>
        <LayoutView Layout="@typeof(MainLayout)">
            <p role="alert">Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

MyLayout.razor:

@inherits LayoutComponentBase
@layout MainLayout

<a href="/someOtherPageWithoutThisLayout">Go Back</a>

@Body

@code {
    [CascadingParameter] RouteData RouteData { get; set; }
}

目标组件的生命周期如下所示:

MyComponent constructed: HashCode=216708300
MyComponent OnInitializedAsync: HashCode=216708300
MyComponent constructed: HashCode=785069820
MyComponent OnInitializedAsync: HashCode=785069820
MyComponent disposing: HashCode=216708300

Answer changed as original contained the wrong information.

我认为你的问题出在这里 - 我第一次检查代码时错过了添加的级联!

<CascadingValue Value="@routeData">
  <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</CascadingValue>

长话短说,我们无法控制渲染过程,渲染器才是。我认为正在发生的是添加的级联导致渲染器以错误的顺序更新渲染树(及其中的组件)。你造成了一点短路。渲染器将​​ ThingList 添加到 ThingLayout。然后它替换 ThingLayout 并将其与 ThingList.

的初始实例一起处理

如果您需要 RouteData 在您的组件中使用 DI 范围的服务来传递它。

没有修复,因为它不是真正的错误。这是边缘条件行为:你正在做一些意想不到的事情。

以下方法似乎可以解决此问题。它依赖于通过 Body 访问 RouteData 而不是注入它。

protected override void OnInitialized()
{
    if (Body?.Target is RouteView routeView)
    {
        var value = routeView.RouteData.RouteValues["routeParameterName"];
    }
}

编辑 1:上述方法不适用于嵌套布局。以下示例处理了这些情况。

public static bool TryGetRouteData(this Delegate renderFragment, out RouteData routeData)
    {
    routeData = null;

    if (renderFragment?.Target is RouteView routeView)
    {
        routeData = routeView.RouteData;
    }
    else if (renderFragment?.Target is object target)
    {
        // the target may be a CompilerGenerated class
        var bodyField = target.GetType().GetField("bodyParam");

        if (bodyField is not null)
        {
            var value = bodyField.GetValue(target);

            if (value is Delegate childDel)
            {
                TryGetRouteData(childDel, out routeData);
            }
        }
    }

    return routeData is not null;
}

然后在 layout/component class:

protected override void OnInitialized()
{
   if (Body?.TryGetRouteData(out var routeData) == true)
   {
       // stuff here
    }
}