何时在 Blazor 组件中调用 StateHasChanged()

Posted

技术标签:

【中文标题】何时在 Blazor 组件中调用 StateHasChanged()【英文标题】:when to call StateHasChanged() in Blazor component 【发布时间】:2021-11-07 03:53:32 【问题描述】:

我想弄清楚何时需要调用 StateHasChanged()。我知道通过 javascript 更改值时,可能需要强制 blazor 组件呈现页面。在下面的代码中,我有一个名为 HandleClick 的方法来处理按钮单击,我故意注释掉 StateHasChanged() 。 HandleClick 方法本身调用Javascript 来设置Part 2 中元素的值。由于 Blazor 组件不知道 fromJs 值的变化,所以 Part 1 中的值在点击后不会更新。但问题是为什么当我再次单击按钮时 Part 1 中的值会发生变化。

@page "/ExampleY"

@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime

<div class="p-2">
    <p class="alert-info p-3">
       Part 1.  Get value via JS call: <strong>@(fromJs ?? "Empty")</strong>
    </p>

    <p class="alert-success p-3">
        Part 2. Set value via JS call: <strong @ref="element"></strong>
    </p>

    <button class="btn btn-success" @onclick="HandleClick">Click to Call JS</button>
</div>

@code 
    private string fromJs;
    private ElementReference element;

    public async void HandleClick()
    
        fromJs = await JSRuntime.InvokeAsync<string>("setText", element, "Hello from JS call!");
        // StateHasChanged();
    


【问题讨论】:

【参考方案1】:

这与 JavaScript 互操作无关。

当您进行此更改时:

//public async void HandleClick()
  public async Task HandleClick()

那么这两个部分将同时更新。

几乎总是避免async void。它是 Timer.Elapsed 等老式事件处理程序的最后手段。 Blazor 事件可以返回 void[async] Task

问题是为什么当我再次单击按钮时第 1 部分中的值会发生变化。

ButtonClick 总是会导致重新渲染。但是由于async void 这可能发生在之前 fromJs 被分配。 await JSRuntime.InvokeAsync(...) 中的 await 允许这样做。在下一次单击时,您将看到 fromJs 的先前值出现在第 1 部分中。 使用"Hello from JS call! "+DateTime.Now 来查看这个版本。

【讨论】:

【参考方案2】:

Blazor“内部”事件处理程序代码如下所示:

var task = InvokeAsync(EventMethod);
StateHasChanged();
if (!task.IsCompleted)

    await task;
    StateHasChanged();

如果您将任务传递为 void,如下所示:

public async void HandleClick()

没有什么可以等待的。 task 已完成,最终的渲染事件永远不会发生。这就是为什么任何async 处理程序(即带有等待的方法),如上所述,必须返回一个任务。

所以回答你的问题。第 1 部分中的值已更新,但在重新渲染组件之后 - 没有要等待的任务。当您再次单击该按钮时,您会显示“最后一个”值,即最后一次单击按钮的值,而不是当前单击。如果您将文本设置为点击时间,您会看到这一点。

【讨论】:

是的,正如您在此处显示的,总是有 1 次调用 StateHasChanged()。第二个用于异步处理程序。我在找这件作品。 @HenkHolterman。您会在许多 Blazor 代码库中看到相同的基本模式 - 就像 ComponentBase 如何运行 OnInitializedAsyncOnParametersSetAsync

以上是关于何时在 Blazor 组件中调用 StateHasChanged()的主要内容,如果未能解决你的问题,请参考以下文章

在 blazor 中使用自定义组件时在注入的差异服务中调用 HttpContextAccesor 后为 null

如何从 Blazor 组件调用“/Identity/Account/ExternalLogin”?

如何从服务器端 Blazor 应用程序中的 Blazor 组件调用 razor 页面而不导致页面刷新

如何在 Blazor 中引用通过 DynamicComponent 创建的组件?

从 Blazor 中的页面组件调用 MainLayout 中的方法

何时在 Blazor 中使用 ValueChanged 和 ValueExpression?