我正在制作一个Blazor服务器端项目,我想创建一个单击后便停用的按钮,但不使用disabled
的{{1}}属性。代码很简单:
<button>
出于奇怪的原因,第一个@functions {
LogInForm logInForm = new LogInForm();
bool IsDisabled;
SignInResult result;
protected override void OnInitialized()
{
IsDisabled = false;
}
async Task TryLogIn()
{
IsDisabled = true;
StateHasChanged();
result = await _LogInService.TryLogIn(logInForm);
Console.WriteLine("Logging status : " + (result.Succeeded ? "Sucess" : "Failure"));
IsDisabled = false;
StateHasChanged();
}
}
没有触发,但是第二个确实重新渲染了页面。通过进入Debug模式并进入StateHasChanged
方法,可以很容易地进行测试。在第二次调用时,它会在进入方法后停止在HTML代码上,但不是第一次。
为什么?
NB:我不是在寻找仅使用StateHasChanged()
或Task.Delay
的变通办法,因为它存在那些线程与UI刷新线程之间的竞争状态,并且因此这不是一个可靠的解决方案。我正在寻找Task.Run(...)
行为或解决方法的答案,方法是使用StateHasChanged()
或PropertyChanged
之类的事件并将按钮作为子组件。
编辑:经过一些测试,看来EventCallback
仅在对StateHasChanged()
进行await
操作之后才触发组件的重新渲染。可以通过在Task
行中添加注释或将result = await _LogInService.TryLogIn(logInForm);
更改为IsDisabled = ...
来轻松地对其进行测试。我现在有一些解决方法,但是我仍然想知道为什么这是一个功能。 await new Task.Run(() => { IsDisabled = ...})
是否应在执行任何操作后重新渲染?还是认为只有StateHasChanged()
个操作(因此大部分是服务器调用)才能更改UI中的某些内容?
答案 0 :(得分:3)
Blazor团队将发布有关StateHasChanged()工作方式的文档。
您可以在这里跟踪它:https://github.com/aspnet/AspNetCore/issues/14591
就目前而言,我认为从github评论中得到的解释是一个很好的解释:
添加对StateHasChanged的调用只是将要排队的组件排队 呈现。渲染器决定何时进行渲染。
这可以由以下四种情况触发:
- 初始化渲染,引导过程触发根组件及其所有子组件的初始渲染。
- 一个事件,其中处理事件的组件会在事件发生后自动触发新的渲染,并且有可能 其子项(如果它渲染新子项或更改其参数)。
- 从InvokeAsync调用中调用StateHasChanged的结果(本质上是编组回UI线程)
- 由于父组件更改了子组件的参数,这是在进行差异化处理时发生的 渲染器在子组件上调用SetParametersAsync。
非常清楚,调用StateHasChanged仅将Render排队。 组件或“将其标记为脏”。
由渲染器决定何时以及如何生成 渲染。 BuildRenderTree不会导致新的渲染输出 在组件的“ V-DOM”的新定义中 被呼叫。
通常,每个渲染批处理一次渲染一个组件(这是一个 一起渲染/差异化并发送到的组件的集合 用于更新的用户界面)。在只有两种情况下,组件 批量渲染多次:
- 您有一个直接实现IComponent并调用RenderHandle.Render的组件
- 您在子级和父级组件之间具有循环依赖关系,这可能导致父级重新呈现为子级的一部分 从父级调用一些回调参数作为其一部分 初始化
来源:https://github.com/aspnet/AspNetCore/issues/15175#issuecomment-544890549
答案 1 :(得分:1)
以下是描述重新渲染如何发生的执行流程:
现在,在将值分配给局部变量IsDisabled之后调用StateHasChanged时,并不会真正更改组件的状态,因此没有理由调用StateHasChanged会产生重新渲染。当您调用StateHasChanged时,它只是将该组件的渲染请求排队。但是没有理由重新渲染...
或者它认为只有异步操作(因此大部分是服务器调用)才能 更改用户界面中的某些内容?
除了OnInitializedAsync方法外,操作的类型是否异步均无关紧要,在这种情况下,当OnInitializedAsync方法完成以再次重新呈现UI时,将自动调用StateHasChanged方法。异步调用检索到的新数据将在OnInitializedAsync方法中执行。
更新:
您想要的东西可以通过多种方式完成,其中最简单的方法如下:
<input type="button" value="Click me now" disabled="@IsDisabled" @onclick="TryLogIn" />
@code{
bool IsDisabled;
protected override void OnInitialized()
{
IsDisabled = false;
}
async Task TryLogIn()
{
IsDisabled = true;
// Do some async work here...
// Note: Replace your async method with Task.Delay
await Task.Delay(5000);
IsDisabled = false;
}
}
这应该有效... 注意:禁用按钮控件的唯一方法是使用disabled属性
无需调用StateHasChanged方法。当编译器(编译器)为您的组件创建EventCallback“委托”时,编译器插入您的源代码中的代码会自动调用它。
在触发UI事件后,将自动调用StateHasChanged方法。