Blazor中的StateHasChanged不起作用

3
@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的值时,它才会改变,但如果您仅更改一次,则可以正常工作。但是,我确实需要这样做两次。因此,我制作了一个进度条。如何修复它?


2
@viveknuna 那是一个糟糕的建议。 - undefined
2
因为它会阻塞线程。是的,有更好的选择 - undefined
3
你不需要等待1000ms,但是每次重新渲染时,你确实需要await一些东西 - 通常只需要await Task.Delay(1)或者await Task.Yield() - 来让出线程并允许渲染器处理来自StateHasChanged()的请求。 - undefined
1
你在使用哪个进度条组件或脚本?search是做什么的?为什么它不是异步的?任何可能会阻塞的Blazor调用都应该是异步的,比如调用远程服务。这意���着search()很可能是有意地阻塞了一个异步操作。 - undefined
1
@viveknuna 那是糟糕的建议,倍增地糟糕。它本来就很糟糕。浏览器标签是单线程的,所以 Thread.Sleep 会完全冻结整个标签,没有任何好处。 - undefined
显示剩余8条评论
2个回答

4

StateHasChanged()方法不会执行或强制执行渲染,它只是请求一个渲染。
这个请求只有在主线程被释放一段时间后才能被响应,要么是在await中,要么是在事件处理完成后。

当你没有任何异步工作时,可以使用Task.Delay(1)来模拟异步。但是你必须await,所以你的方法必须返回Task并且本身也必须被await。

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
}

同意你的观点,但如果看不到每个方法的具体作用,就很难知道。在回答问题时,我尽量避免做太多假设。我的方法是异步的,如果唯一的方式是添加一个 await Task.Delay 那也可以。我加上了三秒钟的延迟,这样你就可以看到它的工作过程。通常我会使用 Task.Yield,但关于它的使用有很多谣言和迷信。 - undefined
你可以看到他们并不受期待。而且必须有所作为,否则界面将无法移动。 - undefined
你可以假设,但不能确定。search();可能会出现异步警告。我已经吃过亏了!是的,某些东西需要让出来才能更新UI! - undefined
1
@MrCakaShaunCurtis 如果在search()函数上出现了异步警告,那么它仍然没有被等待,也就是说仍然以同步方式调用,这意味着执行仍然无法返回给渲染器以进行渲染。因此,search()是否声明为async并不重要,重要的是它没有被等待。 - undefined

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

关键在于,如果您的事件返回一个任务并且使用yield,则仅会发生第一次渲染。
  1. 如果您的事件使用yield但返回void,则没有任务供处理程序等待,因此在事件完成之前运行最后一次渲染。
  2. 如果您的代码是同步的,即没有yield,则它会完成,因此任务已完成,只会发生第二次渲染。

你的伪代码有点模糊。不过,你的代码是正确的,这就是编码的方式。 - undefined
.Clear()方法是同步的,Task.Delay()无法模拟这一点。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接