查看此代码(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>
当您不等待异步方法时,执行将继续,而无需等待任务完成。基本上,这是代码中发生的情况:
- 单击按钮
- 读取数据库调用
- 状态已更改由框架调用
str
尚未更新,因此 UI 不会刷新str
终于更新了- 您需要手动调用
StateHasChanged
来更新 UI
您的版本缺少的是return
,这将起作用:
<button @onclick="eventArgs => { return OnClick(); }">Hello</button>
您可以缩短为:
<button @onclick="eventArgs => OnClick()">Hello</button>
当没有参数时,您可以将其简化为仅
<button @onclick="OnClick">Hello</button>
在所有三种情况下,分配给@onclick
的方法现在都会返回一个任务,并且将被等待。然后你不再需要StatehasChanged();
线了。
生成的@onclick=
代码适用于接受Task
或void
方法并自动使用 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
方法。