【问题标题】:When should I call StateHasChanged and when Blazor automatically intercepts that something is changed?我应该何时调用 StateHasChanged 以及 Blazor 何时自动拦截某些更改?
【发布时间】:2020-05-22 13:30:12
【问题描述】:

我很难理解何时应该调用 StateHasChanged() 以及何时 Blazor 拦截到某些内容已更改,因此必须重新渲染。

我创建了一个示例项目,其中包含一个按钮和一个名为 AddItem 的自定义组件。该组件包含一个带有红色边框的 div 和一个按钮。

我的预期:我希望 AddItem 的 div 在用户单击索引页面中包含的按钮时显示出来。然后我想在用户单击 AddItem 的按钮时隐藏它。

注意: AddItem 不会在外部公开_isVisible 标志,而是包含Show() 方法。所以AddItems.Show()会在索引按钮被点击时被调用。

测试:

  1. 我单击 Index 的单击按钮,然后调用方法 Open()AddItem.Show()。标志_isVisible 设置为true,但没有任何反应,并且调用了Index 的ShouldRender()

    控制台输出:

    • 渲染索引
  2. 我已将AddItem.Show() 修改为public void Show() {_isVisible = true; StateHasChanged();}。现在 AddItem 的 div 按预期显示和隐藏。

    控制台输出:

    • 渲染 AddItem(1° 点击索引按钮)
    • 渲染索引(1° 点击索引按钮)
    • 渲染 AddItem(2° 点击 addItem 的关闭按钮)
  3. 我已经用 <AddItem @ref="AddItem" CloseEventCallback="CallBack" /> 修改了 <AddItem @ref="AddItem" />,从 AddItem 的 Show() 方法中删除了 StateHasChanged。现在 AddItem 的 div 按预期显示和隐藏。

基于测试 3: 如果我将 AddItem 的 CloseEventCallback 设置为任何父母的方法,为什么我不必显式 StateHasChanged?我很难理解它,因为 AddItem 不会在任何地方调用 CloseEventCallback

当 Blazor 了解某些内容已更改,因此必须重新渲染时?

我的示例代码(如果你想试试的话)。

我的索引.razor

<AddItem @ref="AddItem" />
<button @onclick="Open">click</button>
@code {
    AddItem AddItem;

    public void Open()
    {
        AddItem.Show();
    }

    public void CallBack()
    {
    }

    protected override bool ShouldRender()
    {
        Console.WriteLine("Render INDEX");
        return base.ShouldRender();
    }
}

我的 AddItem 组件

@if (_visible)
{
    <div style="width: 100px; height: 100px; border: 1px solid red">testo</div>
    <button @onclick="Close">close</button>    
}

@code {
    private bool _visible = false;

    [Parameter] public EventCallback<bool> CloseEventCallback { get; set; }

    public void Show()
    {
        _visible = true;
    }

    public void Close()
    {
        _visible = false;
    }

    protected override bool ShouldRender()
    {
        Console.WriteLine("Render ADDITEM");
        return base.ShouldRender();
    }
}

【问题讨论】:

    标签: blazor


    【解决方案1】:

    一般来说,StateHasChanged()方法是在UI事件触发后自动调用的, 例如,单击按钮元素后,会引发单击事件,并且 StateHasChanged() 方法是 自动调用以通知组件其状态已更改并且应该重新渲染。

    最初访问索引组件时。先渲染父组件,再渲染父组件 渲染它的孩子。

    每当单击“打开”按钮时,索引组件都会重新渲染(这是因为 事件的目标是父组件,默认情况下会重新渲染 (无需使用 StateHasChanged)。但不是孩子,他不知道他的 状态发生了变化。为了让孩子意识到他的状态已经改变,并且 应该重新渲染,您应该添加对 StateHasChanged 方法的调用 手动在 Show 方法中。现在,当您单击“打开”按钮时,子组件是 首先重新渲染,然后其父级重新渲染。现在红色 div 呈现可见。

    单击“关闭”按钮隐藏红色 div。这次只有子组件重新渲染 (这是因为事件的目标是子组件,默认会重新渲染), 但不是父级。

    这种行为是正确的,并且是设计使然。

    如果您从 AddItem.Show 方法中删除对 StateHasChanged 方法的调用,请定义此 属性:[Parameter] public EventCallback&lt;bool&gt; CloseEventCallback { get; set; },并添加 父组件中的组件属性为该属性赋值,如下所示: &lt;AddItem @ref="AddItem" CloseEventCallback="CallBack" /&gt;,你会注意到外表没有变化, 但是这次单击“打开”按钮时重新渲染的顺序是首先是父级 重新渲染,然后孩子重新渲染。这准确描述了您发现表达的问题 在您来自 cmets 的问题中:

    那么,为什么即使 CloseEventCallback 没有在任何地方调用,我的测试 3 也会按预期工作?

    你是对的......在进行进一步调查之前,我无法真正解释这种行为。 我会试着找出发生了什么,并让你知道。

    AddItem 的 close 方法调用 CloseEventCallback 来通知父级它应该重新渲染。

    注意:您的代码使用布尔类型说明符定义 CloseEventCallback,因此您必须定义一个方法 在具有布尔参数的父组件中。当您调用 CloseEventCallback 'delegate' 你实际上调用了 Index.Callback 方法,你应该给它传递一个布尔值。当然,如果你通过 组件的值,您希望它重新呈现,以便可以在 UI 中看到新状态。这是 EventCallback 提供的功能:虽然事件是在子组件中触发的, 它的目标是父组件,这会导致父组件重新渲染。

    我想知道如果调用了订阅的 EventCallback 之一,为什么父组件应该重新呈现自己?

    这正是我在上一段中试图解释的内容。 EventCallback 类型是专门为解决事件目标的问题而设计的,将事件路由到组件 其状态已更改(父组件),并重新渲染它。

    【讨论】:

    • 谢谢Enet,现在我更清楚了。我的脑海里还有两个问题。 1. 正如 Blazor Docs 所预期的那样,AddItem 的 close 方法调用 CloseEventCallback 来通知父级它应该重新渲染。那么,即使没有在任何地方调用 CloseEventCallback,为什么我的测试 3 也能按预期工作? 2.我想知道如果调用了其中一个订阅的EventCallback,为什么父组件应该重新渲染自己?我可能有一个与 UI 状态不严格相关的 EventCallback。我知道这是一个奇怪的问题,我只是问,因为我想了解
    • @Leonardo Lurci ,我已经更新了我的答案,以便在上面的评论中提供您的问题。这是一个非常复杂的主题...请不要犹豫,提出问题。
    • 谢谢。我不知道渲染过程中有特定的顺序。当您写 When you invoke the CloseEventCallback [...] you expect it to re-render 时,我同意您的看法,确实我是一个奇怪的问题,只是为了了解 Blazor 引擎下发生了什么。如果可能,我会问你,你在哪里找到这些信息:在 GitHub 上寻找 asp.net Core Blazor?我正在尝试学习 Blazor,我想了解的不仅仅是“如何使用 Blazor / 你可以用 Blazor 做什么”。你有什么建议吗?如果您在我的测试 3 中发现任何内容,请告诉我。
    • 我的学习来源是不久前很差的文档,stackoverflow中的回答问题,以及github中blazor的issues部分。我不访问其他来源,例如博客,也不会在第三方组件上浪费时间。我完全专注于 Blazor 组件模型以及如何使用它。我还从 Blazor 团队创建的代码示例中学到了很多东西,例如 FlightFinder、Blazing Pizza 等。
    • 最好的材料是由史蒂夫安德森提供的......只要去他的存储库,阅读他的样本,分析它们,运行它们等等。这是 rynowak 提供的问题的链接,其中之一Blazor 团队,关于 EventCallback。确保您阅读了所有内容:github.com/dotnet/aspnetcore/issues/6351
    【解决方案2】:

    组件必须在首次添加到组件时呈现 父组件的层次结构。这是唯一一次 组件必须渲染。组件可能会在其他时间渲染 符合他们自己的逻辑和约定。

    SPA 应用程序遵循以下组件化架构:

    将该树中的每个节点视为我们在 blazor 中的一个组件。拥有组件父母和他们的孩子。

    StateHasChanged 由 ComponentBase(任何 blazor 组件的标准类)接收。这是包含在以下时间自动触发重新渲染的逻辑:

    • 从父组件应用一组更新的参数后。
    • 应用级联参数的更新值后。之后
    • 事件通知并调用其自己的事件处理程序之一。
    • 在调用自己的 StateHasChanged 方法后(请参阅 ASP.NET Core
      Razor 组件生命周期)。

    与 blazor 的自动渲染调用不同,我们使用 StateHasChanged。这是当框架本身在上述时间之一渲染时,它只渲染它的子组件(子组件)。当我们调用 StateHasChanged。大多数从 ComponentBase 继承的组件都会被渲染,无论它们在树中的位置如何。 (不必要的重新渲染)。因此,我们应该避免使用 StateHasChanged,让框架处理渲染。

    调用 StateHasChanged 有意义的场合:

    • 异步处理程序涉及多个异步阶段
    • 接收来自 Blazor 渲染外部的调用和 事件处理系统
    • 在由 a 重新渲染的子树之外渲染组件 特定事件

    了解更多:https://docs.microsoft.com/en-us/aspnet/core/blazor/components/rendering?view=aspnetcore-5.0

    【讨论】:

      猜你喜欢
      • 2021-11-07
      • 2020-07-13
      • 1970-01-01
      • 2021-06-08
      • 2015-04-02
      • 2020-08-07
      • 2016-04-30
      • 2020-12-21
      相关资源
      最近更新 更多