【问题标题】:Unexpected UI binding behavior in Blazor WASMBlazor WASM 中的意外 UI 绑定行为
【发布时间】:2021-06-09 18:55:18
【问题描述】:

我在 Blazor WASM 和 Blazor 服务器之间的 UI 绑定行为中遇到了意外(且莫名其妙)的差异。鉴于以下 razor 组件,在 Blazor Server 和 Blazor WASM 中,“发送”按钮在请求期间被正确禁用:

<MudButton Disabled="isProcessing" OnClick="Submit">Send</MudButton>

@code {
    private bool isProcessing;

    private async Task Submit()
    {
        isProcessing = true;
        var contacts = await Mediator.Send(getContacts);
        isProcessing = false;
    }    
}

在向提交方法添加第二个等待调用 (IsValidAsync()) 后,按钮现在在 Blazor WASM 中的请求期间保持启用,我认为这是错误的。在 Blazor Server 中,它仍然按预期被禁用:

private async Task Submit()
{
    if (await validator.IsValidAsync())
    {
        isProcessing = true;
        var contacts = await Mediator.Send(getContacts);
        isProcessing = false;
    }
}

这种绑定在 Blazor WASM 中不起作用是否有明确的原因?或者这是 Blazor 中的一个明显错误?

【问题讨论】:

    标签: c# blazor blazor-server-side blazor-webassembly


    【解决方案1】:

    规则是您可以免费获得 2 个隐含的 StatehasChanged() 调用,1 个在事件处理程序之前和 1 个之后。

    因此,您在第一次等待时会获得“免费更新”,但是当您的代码中有更多步骤(需要输出)时,您需要提供帮助:

    private async Task Submit()
    {
       if (await validator.IsValidAsync())  // consumes the first StateHasChanged
       {
          isProcessing = true;
    
          StateHasChanged();    // request an update
    
          var contacts = await Mediator.Send(getContacts); // update UI in background
          isProcessing = false;     
       }
       // one implied StateHasChanged for free again
    }
    

    这个模式应该用在 Server 和 Wasm 上。原始代码在服务器上为您工作的原因尚不清楚。这可能是 IsValidAsync() 的侥幸,具有非异步代码路径。

    【讨论】:

    • 非常感谢。这是相当棘手的。与 VueJS 等相比,我认为 Blazor 在这方面有一些改进。
    • 也许,但有一个性能角度。 isProcessing 不需要(昂贵的)绑定。一旦你知道了规则,它就很有用了。
    【解决方案2】:

    我很惊讶它的行为方式不同,但 Server 和 WASM 之间存在显着差异,这可以解释发生了什么。我对 MudBlazor 不是很熟悉,所以我不确定 MudBlazor 内部是如何工作的。

    不同之处在于浏览器对于所有操作只有一个线程。服务器与服务器实例上可用的一样多,几乎所有情况都不止一个。

    试试:

    private async Task Submit()
    {
        isProcessing = true;
        if (await validator.IsValidAsync())
        {
            var contacts = await Mediator.Send(getContacts);
        }
        isProcessing = false;
    }
    

    private async Task Submit()
    {
        if (await validator.IsValidAsync())
        {
            isProcessing = true;
            await InvokeAsync(StateHasChanged);
            var contacts = await Mediator.Send(getContacts);
            isProcessing = false;
        }
    }
    

    事件处理程序基本上是这样做的:

    var task = Submit();
    StateHasChanged();
    if (!task.IsCompleted || !task.IsCanceled)
    {
      await task;
      StateHasChanged();
    }
    

    您可以在ComponentBase 中看到这种模式。它说如果任务成功,那么我们更新 UI,等待它完成,然后再次更新 UI。如果它没有产生,那么我们会在它完成后更新 UI。我的服务器代码和 WASM 代码的行为相同 - 确定您的代码有何不同。

    您的代码在if (await validator.IsValidAsync()) 中产生,因此第一个StateHasChanged 在设置isProcessing 之前被调用。

    【讨论】:

    • 太棒了,您的第一个建议确实有效。但这种现象确实让我怀疑 Blazor WASM 的生产准备情况。对于这种不稳定的行为,我没有看到任何合理的解释:-/ 我试图将整个“isProcessing”模式封装到 UI 绑定的请求对象中。现在似乎不可能,因为 req 对象永远不会告诉 razor 组件更新自己(我试图避免必须为每个视图编写相同的处理模式)
    • await InvokeAsync(StateHasChanged); 仅在例如 Timer 事件中需要。在 ButtonClick 中,只需使用 StateHasChanged(); 。 Blazor 有自己的 SyncContext,让我们使用它。
    • 查看添加的 cmets 以了解事件处理程序如何运行。 InvokeAsync(StateHasChanged); 我同意,但你永远不知道你的代码会被复制到哪里,所以我在“公共”代码中引用了带和大括号的方法。
    猜你喜欢
    • 1970-01-01
    • 2021-09-27
    • 1970-01-01
    • 1970-01-01
    • 2020-12-25
    • 2013-08-03
    • 2021-07-16
    • 1970-01-01
    • 2022-12-21
    相关资源
    最近更新 更多