如果方法包含等待调用,为什么我需要调用 StateHasChanged



查看此代码(blazor 服务器页):

@page "/"
<div>@str</div>
<button @onclick="eventArgs => { OnClick(); }">Hello</button>
@code
{
private string str { get; set; }
private void OnClick()
{
str = "Hello world";
}
}

此代码完美运行。当我点击按钮时,我可以在我的页面上看到"你好世界"。

现在看看这段代码:

@page "/"
<div>@str</div>
<button @onclick="eventArgs => { OnClick(); }">Hello</button>
@code
{
private string str { get; set; }
private async Task OnClick()
{
await ReadDataBase();
str = "Hello world";
StateHasChanged();
}
}

我不明白为什么,但是如果我不输入状态已更改(),str 不会立即刷新。我的问题是...为什么?

谢谢

原因是您没有在 lambda 中await异步方法。换句话说,将您的代码更改为以下内容:

<button @onclick="async eventArgs => { await OnClick(); }">Hello</button>

话虽如此,如果您所做的只是调用异步方法并且从未实际使用等待的任务,则可以删除括号{ }并将代码更改为以下内容:

<button @onclick="() => OnClick()">Hello</button>

如果你这样做,框架会为你包装一个Func<Task>中的lambda。

现在,我假设您使用lambda是有原因的,例如您想向它传递一些参数。如果不是这种情况,您也可以简单地编写如下代码:

<button @onclick="OnClick">Hello</button>

当您不等待异步方法时,执行将继续,而无需等待任务完成。基本上,这是代码中发生的情况:

  1. 单击按钮
  2. 读取数据库调用
  3. 状态已更改由框架调用
  4. str尚未更新,因此 UI 不会刷新
  5. str终于更新了
  6. 您需要手动调用StateHasChanged来更新 UI

您的版本缺少的是return,这将起作用:

<button @onclick="eventArgs => { return OnClick(); }">Hello</button>

您可以缩短为:

<button @onclick="eventArgs => OnClick()">Hello</button>

当没有参数时,您可以将其简化为仅

<button @onclick="OnClick">Hello</button>

在所有三种情况下,分配给@onclick的方法现在都会返回一个任务,并且将被等待。然后你不再需要StatehasChanged();线了。

生成的@onclick=代码适用于接受Taskvoid方法并自动使用 eventArgs(或不使用)。

我不明白为什么,但是如果我不输入状态已更改(),str 不会立即刷新。我的问题是...为什么?

这部分是正确的。str 变量被分配有字符串"Hello world",并且只有在对ReadDataBase的调用返回后才会重新呈现页面。请注意,应等待对OnClick的调用,如下所示:

`@onclick="async eventArgs => { await OnClick(); }"`

但这不是"str 没有立即刷新"的原因。 原因是在将值str断言之前调用ReadDataBase方法...当你等待ReadDataBase方法时,执行将交给调用代码,框架执行其他任务...只有在ReadDataBase方法返回后(当任务完成时),OnClick方法中的代码才会继续执行下一行,即将字符串"Hello world"分配给str,然后才重新呈现组件,如果您添加对该方法的调用StateHasChanged或者最好还是@onclick="async eventArgs => { await OnClick(); }"并删除对StateHasChanged

结论:如果要为str分配值并立即刷新显示,请将str = "Hello world";放在第一行,await ReadDataBase();放在第二行。无需调用StateHasChanged即可工作,即使您使用

<button @onclick="eventArgs => { OnClick(); }">Hello</button>

而不是

@onclick="async eventArgs => { await OnClick(); }"

顺便说一句,我会做这样的事情:

<button @onclick="eventArgs => OnClick()">Hello</button>

现在尝试了解"eventArgs => OnClick()""eventArgs => { OnClick(); }"之间的区别

试试这个代码:

<div>@str</div>
<button @onclick="OnClick">Click me</button>
@code
{
private string str { get; set; }

private async Task OnClick()
{
str = "Hello world";
// Simulate an async call to ReadDataBase
await Task.Delay(3000);
}
}

更新和澄清:

我想你知道什么时候不手动调用 StateHasChanged 方法,因此你的问题......实际上,在过去,必须在组件的状态更改后手动调用此方法,以便通知组件其状态已更改,并且应该重新呈现。后来,当涉及 UI 事件(如"单击")时,手动调用 StateHasChanged 方法被呈现为超凡脱俗。其背后的原因是,UI 事件几乎总是导致组件状态的修改,因此让我们省去开发人员手动添加它的负担。现在,由于OnClick方法是事件处理程序,因此不需要手动添加 StateHasChanged 方法,但如果不手动添加它,组件就不会刷新(重新呈现)。该行为的原因是同步调用OnClick,而不返回应告知 BlazorOnClick方法已完成的 Task 对象。在这种情况下,Blazor 无法知道OnClick方法何时完成,因此它不会自动调用 StateHasChanged 方法。但是,当您手动添加 StateHasChanged 方法时,组件将刷新(重新呈现)。当然,您不能手动添加它,而是异步调用OnClick方法。

最新更新