Blazor StateHasChanged 无法正常工作

Posted

技术标签:

【中文标题】Blazor StateHasChanged 无法正常工作【英文标题】:Blazor StateHasChanged does not work correctly 【发布时间】:2021-09-03 00:30:26 【问题描述】:
@if (_loading)

    ....

else

    ....

....
@code 

 private bool _loading
...
private void Search()

_loading = false;
StateHasChanged();
Mails.Clear();
Licenses.Clear();
Callings.Clear();
Supports.Clear();
Leads.Clear();
search();
_loading = true;
StateHasChanged();

DOM 在运行时不会改变。仅当您将 _loading 的值更改两次,但仅更改一次时,它才有效。但是,我绝对需要这样做两次。所以我做了一个进度条。如何解决?

【问题讨论】:

@viveknuna 这是一个糟糕的建议。 @viveknuna 因为它阻塞了线程。是的,有better options。 您不需要等待 1000 毫秒,但您确实需要 await 为您想要的每次重新渲染提供一些东西 - 通常只需要 await Task.Delay(1)await Task.Yield() - 产生线程并允许渲染器处理来自 StateHasChanged() 的请求 您使用的是哪个进度条组件或脚本? search 是做什么的?为什么不是异步的?任何可能阻塞的 Blazor 调用都是异步的,例如调用远程服务。这意味着search() 很可能故意阻止了异步操作 @viveknuna 这是一个糟糕的建议,平方。一开始就很可怕。虽然浏览器标签是单线程的,所以Thread.Sleep 将冻结整个标签,绝对没有任何好处。 【参考方案1】:

由于您引用的代码未显示谁调用了搜索,因此这是一个有效的版本,它在组件加载事件和通过按钮单击驱动时使用 Search

注意它是异步的并且使用Tasksawait Task.Delay(3000); 模拟您的代码。

@page "/Demo"
<h3>Demoblock</h3>
@if (_loading)

    <div class="bg-warning p-2 m-2">Loading...</div>

else

    <div class="bg-success p-2 m-2">Loaded</div>

<div class="p-2 m-2">
    <button class="btn btn-dark" @onclick="DoSearch">New Search</button>
</div>
@code

    private bool _loading;

    private async Task Search()
    
        _loading = true;
        // not needed
        //StateHasChanged();
        //Mails.Clear();
        //Licenses.Clear();
        //Callings.Clear();
        //Supports.Clear();
        //Leads.Clear();
        //search();
        //Emulate doing the above work
        await Task.Delay(3000);
        _loading = false;
        // not needed
        // StateHasChanged();
    

    protected async override Task OnInitializedAsync()
    
        await Search();
    

    private async Task DoSearch(MouseEventArgs e)
    
        await Search();
    

在伪代码中发生了什么:

Set up a Task when you run the Event - such as OninitializedAsync, On ParametersSetAsync, Mouse/Keyboard Event
If Task has not completed 

    Render the Component.
    Now Wait for the Task to Complete

Render the Component

这里的关键是,只有当你的事件返回一个任务并产生时才会发生第一次渲染。

    如果您的事件产生但返回 void,则处理程序没有任务等待,因此在事件完成之前运行最后一个渲染。 如果您的代码是同步的,即没有屈服,它会完成,因此任务已完成并且只发生第二次渲染。

【讨论】:

你的伪代码有点模糊。但是,您的代码是正确的,这就是编码的方式。 .Clear() 方法是同步的,Task.Delay() 无法模拟。【参考方案2】:

StateHasChanged() 不执行或强制渲染,它只请求一个。 只有在等待或事件处理完成时,主线程被释放一段时间后,才能兑现该请求。

当您没有任何异步工作时,您可以使用 Task.Delay(1) 模拟它。但是你必须 await 所以你的方法必须返回 Task 并且必须自己等待。

private async Task Search()  // call it from an Async method

  _loading = false;
  //StateHasChanged();  // not needed
  await Task.Delay(1);  // here the UI gets updated
  Mails.Clear();
  Licenses.Clear();
  Callings.Clear();
  Supports.Clear();
  Leads.Clear();
  search();            // tricky naming
  _loading = true;
  //StateHasChanged(); // not needed

【讨论】:

同意你的观点,但是如果你看不到,很难知道每种方法的作用——我在回答时尽量不要做太多假设。我的方法是异步的,如果唯一的方法是添加一个等待 Task.Delay,那么就可以了。我添加了 3 秒,以便您可以看到它正在工作。我通常使用 Task.Yield,但使用它时有很多城市神话和巫术。 你可以看到他们没有等待。有些事情必须做,否则 UI 不会让步。 你可以假设,但你不能确定。 search(); 上可能有异步警告。我已经学会了艰难的方式!是的,需要为 UI 更新做出一些贡献! @MrCakaShaunCurtis 如果search()上有异步警告,仍然没有等待,即仍然同步调用,即执行仍然无法返回渲染器以便它可以渲染.所以search()是否被声明为async并不重要,重要的是它没有被等待。

以上是关于Blazor StateHasChanged 无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章

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

Blazor 组件在 StateHasChanged() 之后未更新

我应该何时调用 StateHasChanged 以及 Blazor 何时自动拦截某些更改?

Blazor 中的 StateHasChanged 目标

使用 StateHasChanged 从父组件刷新 Blazor 服务器子组件

无法让 StateHasChanged 更新 @if 语句