asp.net 由于Blazor变更检测API,组件重新渲染被跳过,如何避免?

7hiiyaii  于 2023-01-14  发布在  .NET
关注(0)|答案(1)|浏览(190)

我在Blazor项目(.NET 5)上工作。我在组件渲染方面遇到了问题。我有一个内部包含ChildContent的父组件。我是这样使用它的:

<ParentComponent>
    <ChildComponent1 Title="Component1"></ChildComponent1>
    <ChildComponent2 Title="Component2" SampleEnum="SampleEnum.Bar"></ChildComponent2>
</ParentComponent>

每个ChildComponent都继承ChildComponentBase

public class ChildComponent1 : ChildComponentBase 
{
   // some code
}

ChildComponentBase包含级联参数ParentComponent和2个参数:其中一个是stringImmutable for Blazor Change Detection API),另一个是enum(不是不可变的),这只是一个例子。

public partial class ChildComponentBase
{
     [CascadingParameter]
     public ParentComponent Parent { get; set; } = default !;

     [Parameter]
     public string? Title { get; set; } // Immutable

     [Parameter]
     public SampleEnum SampleEnum { get; set; } // not Immutable
}

ParentComponent中,我使用了延迟渲染策略。Defer组件看起来像这样,并在ParentComponent中使用:

// This is used to move its body rendering to the end of the render queue so we can collect
// the list of child components first.
public class Defer : ComponentBase
{
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    protected override void BuildRenderTree( RenderTreeBuilder builder )
    {
        builder.AddContent( 0, ChildContent );
    }
}

在我的第一次渲染项目中,我从ChildContent中收集所有ChildComponent,如下所示:

  • 儿童组件底座剃须刀 *
@{
    Parent.AddChild(this); // Parent is cascading parameter
}

然后调用回调函数来处理数据。ParentComponent如下所示:* 父组件.剃刀 *

<CascadingValue Value="this" IsFixed>
    @{
        StartCollectingChildren();
    }
    @ChildContent

    <Defer>
        @{
            FinishCollectingChildren();

            ProcessDataAsync();
        }

        @foreach (var o in _childComponents)
        {
            <p>@o.Title</p>
        }
    </Defer>
</CascadingValue>
  • 父组件.剃刀.cs*
public partial class ParentComponent
{
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    private List<ChildComponentBase> _childComponents = new();
    private bool _firstRender = true;
    private bool _collectingChildren; // Children might re-render themselves arbitrarily. We only want to capture them at a defined time.

    protected async Task ProcessDataAsync()
    {
        if (_firstRender)
        {
            //imitating re-render just like it would be an async call
            await InvokeAsync(StateHasChanged);
            _firstRender = false;
        }
    }

    public void AddChild(ChildComponentBase child)
    {
        _childComponents.Add(child);
    }

    private void StartCollectingChildren()
    {
        _childComponents.Clear();
        _collectingChildren = true;
    }

    private void FinishCollectingChildren()
    {
        _collectingChildren = false;
    }
}

由于调用回调-发生重新渲染。由于重新渲染,StartCollectingChildren()再次被调用。这次在ParentComponent的第二次渲染时,ChildComponent1不会重新渲染,因为Blazor Change Detection API跳过了它(因为它只包含一个不可变参数Title,而ChildComponent2还包含enum参数)。

问题:如何重新渲染此ChildComponent1

我还添加了一个包含上述代码的Sample Project,以便您自己试用。
我尝试了所有我能在谷歌上找到的方法。我找到的最好的解决方法是在第一次渲染时缓存儿童集合,但它看起来很脏,可能会在未来导致问题。

f3temu5u

f3temu5u1#

快速解决问题的方法是修改级联并删除IsFixed
一旦这样做,捕获级联的任何组件都将始终呈现,因为this是一个对象,因此无法通过相等性检查。
您还可以使用Guid级联驱动没有对象参数的子组件上的渲染事件。每当您希望在捕获级联的任何子组件上强制渲染时,请为Map的参数分配新的Guid。

相关问题