如何在 Blazor 中保留组件实例

Posted

技术标签:

【中文标题】如何在 Blazor 中保留组件实例【英文标题】:How to preserve component instances in Blazor 【发布时间】:2021-06-19 21:36:17 【问题描述】:

我有一个类似的组件,我希望在第一次渲染时保存 InnerComponent 的实例,并且每次都渲染相同的实例而不重新实例化它。

@if(isVisible)

    <InnerComponent @ref="@_InnerComponent" @key="@("InnerComponentKey")">
        @ChildContent
    </InnerComponent>


@code
    [Parameter] public InnerComponent _InnerComponent  get; set; 
    private bool IsVisible  get; set; 

当内部组件可见时,用户可以操纵其状态。 但是,如果 IsVisible 设置为 false 然后再次设置为 true,则内部组件将重新渲染以覆盖 _InnerComponent,因此我们将无法跟踪用户对 InnerComponent 实例所做的更改。

添加@key 似乎也无助于保留实例。它只是被重新渲染和覆盖:/ 我敢肯定,我在两次渲染时都给它提供了相同的键,但我不知道如何检查它所比较的​​键。

如果可以渲染组件实例,我可以执行以下操作,但我似乎找不到方法。

@if(isVisible)

    @if(_InnerComponent == null)
    
        <InnerComponent @ref="@_InnerComponent" @key="@("InnerComponentKey")">
            @ChildContent
        </InnerComponent>
    
    else
    
        @_InnerComponent.Render()
    

我对我的问题提出批评,因为我没有问过很多问题:)

提前致谢!

简化示例:

假设我们有以下组件,我将调用 `CounterContainer` ,其中 `&ltCounter&gt` 是默认 Blazor 项目模板中的计数器组件。
@if(CounterIsVisible)

    <Counter @ref="@_Counter" @key="@("CounterKey")" />


<button @onclick="() => CounterIsVisible = !CounterIsVisible">
    Show/Hide counter 
</button>

@code
    [Parameter] public Counter _Counter  get; set; 
    private bool CounterIsVisible  get; set;  = true;

我想保存_Counter 实例,这样我就可以看到我计数的正确_Counter.currentCount。我可以使用this article 中的方法保存它,但我发现它们都不实用,因为

我正在构建的组件拥有比单个变量更多的数据 我只需要CounterContainer 存在且仅用于可视化的数据 对于我的用例来说太复杂了

我已经存储了 Counter 引用。我只想查看它而不是重新实例化它并覆盖整个内容。

希望这让它更清楚一点(:

【问题讨论】:

【参考方案1】:

我很惊讶它可以编译,因为你拼错了你的组件类:

[Parameter] public **InnerCopmonent** _InnerComponent  get; set; 

为什么你的 InnerComponent 有一个 @ref 呢? InnerComponent 是做什么的,为什么要引用它?你能分享那个组件的代码吗?

【讨论】:

嗨 @Bennyboy1973,感谢您的回复 :) 我没有粘贴任何真实代码,所以我猜这个确切的示例(现已编辑)无法编译。 我添加了 @ref 尽管它与问题不是 100% 相关,因为我在我的代码中需要它并且我注意到 @key@ref 有时会奇怪地交互。我觉得它不会增加太多噪音,但是有经验的人会注意到两者都使用是否有问题。 组件太长,无法粘贴到这个问题中,但我只是在一个额外的示例中进行了编辑。希望它能让我的问题更清楚:)【参考方案2】:

最佳选择:单独的视图和数据。您要保留的状态不应保存在组件内,而应保存在单独的 (ViewModel) 对象中。

简短的解决方案:总是渲染组件。使用 @isVisible 打开/关闭 css-class 以隐藏它。

【讨论】:

我喜欢你的第一个选项。第二个似乎有点反 Blazor,在我看来,它最糟糕的罪过是它会起作用,而 OP 从现在开始可能会这样做。 是的,第一个选项很好,我已经有了单独的视图和数据模型。只是我试图保留的信息属于视图模型(Counter 组件),而不是真正属于数据模型。我没有想过使用 ccs,但我不知道它是否是我正在寻找的解决方案。不过感谢您的建议!【参考方案3】:

我喜欢做这种事情的方式是创建一个承载类,我在父类中实例化它并作为Parameter 传递给子类。由于类是通过引用传递的,所以我不需要担心事件或类似的事情。 (我刚刚做了一个演示类来展示你可以拥有任何你想要的东西)

CounterData.cs

public class CounterData
    
        public int counter  get; set; 
        public string Title  get; set;  = "Counter";
        public List<int> CounterHistory  get; set;  = new List<int>();
    

CounterContainer.razor

@page "/counter"

<h3>Counter Container</h3>

<button @onclick="()=> ShowCounter=!ShowCounter">Toggle</button>

@if (ShowCounter)

    <Counter CarryingInstance="PersistentData" />


@code 
    CounterData PersistentData = new CounterData();
    bool ShowCounter = false;

Counter.razor

<h3>@CarryingInstance.Title</h3>

<input @bind="CarryingInstance.Title" /><br />
<button @onclick="()=> Increment(1)">Add</button>
<button @onclick="()=>Increment(-1)">Sub</button>
@CarryingInstance.counter <br />


History:

@foreach (var item in CarryingInstance.CounterHistory)

    <span>&nbsp; @item</span>


@code 
    [Parameter]
    public CounterData CarryingInstance  get; set; 

    void Increment (int amount)
    
        CarryingInstance.counter += amount;
        CarryingInstance.CounterHistory.Add(CarryingInstance.counter);
    

【讨论】:

感谢您的详尽回答!我确实有一个类似于CounterData 的类,但我试图保留的信息并不真正属于CounterData 对象,而是属于Counter 组件(它只是具有相同的视觉数据范围为“ConterContainer”)【参考方案4】:

我已经看到了使用 CSS 隐藏组件的选项以及它可能导致的意外问题,因为当组件被合法地重新渲染/重新实例化时,它们不会经历正常的生命周期事件。所以我正忙着更新几个组件来修复那个确切的东西。我的建议是尽可能避免这样做。

另一种方式,类似于 BennyBoys 携带实例方法,您可以通过使用依赖注入并将类型注册为作用域或单例服务并将其注入到组件中来实现。

您可以在此处阅读 Chris Sainty 的博客,了解不同注册在 blazor 中的工作原理及其行为,以及它是否符合您的需求。

https://chrissainty.com/service-lifetimes-in-blazor/

我会通过官方文档仔细检查它们的行为方式是否仍然相同,但以防万一,但这可能是一种很好的方式。我认为这取决于您的实际用例是这样做是否是一个好主意,但这是另一种选择:)

【讨论】:

是的,但我绝对不想在服务中注册这些数据。这对我的用例来说太复杂了:/ 不过感谢您的建议!

以上是关于如何在 Blazor 中保留组件实例的主要内容,如果未能解决你的问题,请参考以下文章

Blazor如何调用JS方法,C#与JS互操作

Blazor 的依赖注入问题

API 控制器是不是使用与 Blazor 组件不同的服务实例?

Blazor 页面组件

从 JavaScript 渲染 Blazor 组件 - 如何在组件渲染后从 JavaScript 更新 Blazor 组件参数

如何在 Blazor 的可重用表组件中显示数据