调用StateHasChanged()
方法可以呈现组件UI,使用StateHasChanged
方法也会增加成本开销。在Razor组件的呈现中,大部分的方法是不需要
通常情况下,Blazor会在状态变化时自动触发重新渲染,因此只有在需要显式控制重新渲染时(如异步操作或外部事件处理)才需要使用 StateHasChanged
。
如果在 Blazor 的生命周期方法(如 OnInitializedAsync
或 OnParametersSetAsync
)中,框架会自动检测并调用 StateHasChanged
,因此在这些地方不需要手动调用。
自动呈现由ComponentBase 基类完成。
需要手动调用的时机 (1) 在异步处理程序中调用了多个异步方法。 (2) 在 Blazor 不受管理的外部调用事件处理程序。
下面我们通过例子来看看什么时候需要调用
@page "/injectPage"
@rendermode InteractiveAuto
<h3>InjectPage</h3>
<button @onclick="ShowName">修改</button>
<p>@name</p>
@code {
string name = "0";
public async Task ShowName(){
name = "1";
await Task.Delay(2000);
name = "2";
await Task.Delay(2000);
name = "3";
}
}
上面的代码如下,我们期望的结果应该是为点击时,显示数字为0,点击按钮后变为1,之后依次变化为2,3。我们运行代码来看看结果
我们发现调用后的结果和我们预测的不太一样,ui并没有显示中间过程,点击按钮后变化为1,之后直接变化为3了,没有变化为2的过程。
ComponentBase 只在第一次返回 Task,在 Task 已完成的情况下会触发重新呈现,若还有其它 await 等待,则必须手动调用 StateHasChanged()方法才能呈现。
在点击后变化为1,执行的是同步方法,之后等待结束后设置值为2,因为在中间过程不会重新呈现所以界面没有更新为2,等待Task全部结束后进行呈现,这是值已经被修改为3。我们这次调用下StateHasChanged()方法,来看看结果怎么样,代码修改如下:
@page "/injectPage"
@rendermode InteractiveAuto
<h3>InjectPage</h3>
<button @onclick="ShowName">修改</button>
<p>@name</p>
@code {
string name = "0";
public async Task ShowName(){
name = "1";
await Task.Delay(2000);
name = "2";
StateHasChanged();
await Task.Delay(2000);
name = "3";
}
}
这次我们在设置值2之后,调用了StateHasChanged来刷新UI,我们试试效果如何
这次我们发现是符合我们预期的结果了,我们在这种场景下可以去使用StateHasChanged方法来刷新UI。
ComponentBase 只能管理自己生命周期内的方法(如 OnInitialized()或 OnParametersSetAsync())和 Blazor 触发的事件(如@onclick、@onchange 等)。 下面我们用一个例子看看,这个例子中我们创建一个Timer来调用值发生变化。代码如下
@page "/injectPage"
@rendermode InteractiveAuto
@inject ILogger<InjectPage> logger;
<h3>InjectPage</h3>
<button>修改</button>
<p>@name</p>
@code {
string name = "0";
Timer timer;
protected override void OnInitialized()
{
timer = new Timer(OnTimerCallback, null, 0, 4000);
base.OnInitialized();
}
private void OnTimerCallback(object? state)
{
_ = InvokeAsync(() =>
{
logger.LogInformation("方法已经执行");
name = "2";
});
}
}
看看执行的结果
我们看到执行结果,在定时器中的调用已经被执行但是页面UI没有被刷新。 我们修改之前的代码添加使用StateHasChanged方法来看看结果
@page "/injectPage"
@rendermode InteractiveAuto
@inject ILogger<InjectPage> logger;
<h3>InjectPage</h3>
<button>修改</button>
<p>@name</p>
@code {
string name = "0";
Timer timer;
protected override void OnInitialized()
{
timer = new Timer(OnTimerCallback, null, 0, 4000);
base.OnInitialized();
}
private void OnTimerCallback(object? state)
{
_ = InvokeAsync(() =>
{
logger.LogInformation("方法已经执行");
name = "2";
StateHasChanged();
});
}
}
这次我们看到UI就可以刷新了。 timer 计时器上挂载的事件处理程序是 OnTimerCallback(),该事件处理程序是由计时器调用的,并不是由 ComponentBase 管理的,所以不会自动呈现 ,OnTimerCallback()事件中修改的的 UI 上呈现的数据,需要调用StateHasChanged()方法重新呈现。