我应该什么时候调用 StateHasChanged,什么时候 Blazor 会自动拦截某些更改?

Leo*_*rci 32 blazor

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

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

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

注意: AddItem 不会将它的_isVisible标志暴露在外面,而是包含一个Show()方法。SoAddItems.Show()将在单击索引按钮时调用。

测试:

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

    控制台输出:

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

    控制台输出:

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

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

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

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

我的 Index.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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我的 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

Isa*_*aac 18

一般来说,触发UI事件后会自动调用StateHasChanged()方法,比如点击按钮元素后,会引发click事件,会自动调用StateHasChanged()方法通知组件它的状态已经改变,它应该重新渲染。

最初访问 Index 组件时。父组件先渲染,然后父组件渲染子组件。

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

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

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

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

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

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

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

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

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

这正是我在上面的段落中试图解释的。EventCallback 类型是专门为解决事件目标的问题而设计的,将事件路由到状态发生变化的组件(父组件),并重新渲染它。

  • 最好的材料由 Steve Anderson 提供...只需访问他的存储库、阅读他的示例、分析它们、运行它们等。这里是 Blazor 团队之一 rynowak 提供的有关 EventCallback 的问题的链接。确保您阅读全部内容:https://github.com/dotnet/aspnetcore/issues/6351 (2认同)

小智 10

当父组件首次将组件添加到组件层次结构时,组件必须进行渲染。这是组件必须渲染的唯一一次。组件可以根据自己的逻辑和约定在其他时间呈现。

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

代表 SPA 架构的节点图,具有根节点和叶子,一些节点是父节点,一些节点是子节点

将该树中的每个节点视为 Blazor 应用程序中我们的组件。该组件有一个父组件,并且可以有子组件。

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

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

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

调用 StateHasChanged 有意义的场合:

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

了解更多: