本文介绍了Blazor StateHasChanged和子参数(`等待Task.Run(StateHasChanged);`vs`等待InvokeAsync(StateHasChanged);`)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近就Blazor wasmhere中的await Task.Run(StateHasChanged);await InvokeAsync(StateHasChanged);之间的区别提出了一个问题。

结论是await Task.Run(StateHasChanged);是不正确的,应该避免;使用它会产生与await InvokeAsync(StateHasChanged);相同的结果,但是当线程可用时会出现问题(公认的答案会详细说明)。

我已将我的代码库更新为使用await InvokeAsync(StateHasChanged);,但是我发现两者的结果实际上存在差异。

以下是我的应用程序中问题的最小复制:

家长

<h1>Parent: @title</h1>

<button class="btn btn-primary" @onclick="() => SetBool(true)">True</button>
<button class="btn btn-primary" @onclick="() => SetBool(false)">False</button>

<Child Bool="@Bool" @ref="Child"></Child>

@code {
    private bool Bool = false;
    private Child Child;
    private string title;

    private async void SetBool(bool name)
    {
        Bool = name;
        title = Bool ? "True" : "False";
        // NOTE: This will work as expected; Child will be updated with the Parent
        // await Task.Run(StateHasChanged);
        // NOTE: This will only update the Child the second time it is clicked
        await InvokeAsync(StateHasChanged);
        await Child.Trigger();
    }

}

子级

<h3>Child: @title</h3>

@code {
    [Parameter]
    public bool Bool { set; get; } = false;
    private string title;

    public async Task Trigger()
    {
        title = Bool ? "True" : "False";
        await InvokeAsync(StateHasChanged);
    }
}
单击父项中的TrueFalse按钮应同时更新父项和子项中的Bool值。请注意,title变量仅用于可视化显示Bool值。

使用await Task.Run(StateHasChanged);将导致父对象和子对象的状态同时更新。另一方面,await InvokeAsync(StateHasChanged);将更新父组件,但不会更新子组件;需要单击两次a按钮才能获得子组件中各自的值。

我将此值传递给子组件的方式有问题吗?

请注意,不能使用await Task.Run(StateHasChanged);;这样做意味着我无法使用bUnit测试组件。

复制代码可用here

推荐答案

以下代码片段介绍了应该如何编码:

Parent.razor

<h1>Parent: @title</h1>

<button class="btn btn-primary" @onclick="() => SetBool(true)">True</button>
<button class="btn btn-primary" @onclick="() => SetBool(false)">False</button>

<Child Bool="@Bool" @ref="Child"></Child>

@code {
    private bool Bool = false;
    private Child Child;
    private string title;

    protected override void OnInitialized()
    {
        title = Bool ? "True" : "False";
    }

    private async Task SetBool(bool name)
    {
        Bool = name;
        title = Bool ? "True" : "False";

        await Task.CompletedTask;
    }

}

Child.razor

<h3>Child: @title</h3>

@code {
    [Parameter]
    public bool Bool { set; get; } = false;
    private string title;

    private bool _Bool;

    public async Task Trigger()
    {
        title = _Bool ? "True" : "False";
        await InvokeAsync(StateHasChanged);
    }

    protected override async Task OnParametersSetAsync()
    {
        _Bool = Bool;
        await Trigger();

    }
}
注意:您面临的问题不是因为使用Task.Run(StateHasChanged); and await InvokeAsync(StateHasChanged);,尽管结果不同。您根本没有遵循Blazor组件模型规则。

  1. 您是否意识到,当您单击SetBool按钮时,父组件将重新呈现,因为它的状态已更改。因此,子组件使用Bool==true重新呈现,但是您在屏幕上看不到这一点,因为更改子组件中的标题的代码被放在Trigger方法中,该方法仅从父组件调用。您的假设是错误的。

  2. 请勿修改或更改Bool参数属性的状态。定义一个局部变量来存储参数属性的值,您可以随心所欲地操作其值。换句话说,零部件参数特性应定义为自动特性.它们是Blazor框架将值从父组件传递到其子组件的DTO。

  3. 不要不必要地从UI事件处理程序调用StateHasChanged方法。自动调用。

  4. 请勿使用async void.使用async Task.当您使用async void时,Blazor不会计算异步方法何时完成,因此它不会调用StateHasChanged方法

  5. 避免使用Task.Run。我从来不用。我甚至不记得为什么我不应该用它吧。我就是不用它。

这篇关于Blazor StateHasChanged和子参数(`等待Task.Run(StateHasChanged);`vs`等待InvokeAsync(StateHasChanged);`)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

05-17 17:51