.net 正在等待重新呈现InvokeAsync(StateHasChanged)

ecbunoof  于 2023-04-13  发布在  .NET
关注(0)|答案(1)|浏览(185)

我有一个ImagesContainer组件,显示ImageCard组件的分页列表,它是从数据库服务调用填充的。当用户选择一个新页面时,我调用该服务并获取与页码和页面大小相关的更新列表。
由于我设置的一些标记条件(在页面加载和其他场景中显示微调器等),我必须依赖await InvokeAsync(StateHasChanged)来更新页面。
现在我想实现一个选择系统,在ImageCard上用复选框选择一些图像。我有一个Manager Service,它处理应用程序状态和事件,它包含一个List<int>,包含所选图像的id,以及相关的方法来添加,移除并清除复选框状态更改时从ImageCard调用的列表。根据所述列表元素计数隐藏交互按钮。
ImagesContainer从分页更新后,我需要更新其中的每个组件以反映复选框的状态,否则用户选中的子ImageCard组件将保留其选中状态,而不管它的新Image.Id是否存在于Manager Service的选定图像列表中,即使实际的IMG元素在组件上改变。
为了处理这个问题,我创建了一个OnRefreshImages事件,它是通过Manager Service从分页方法触发的,并由ImageCard使用,当它在组件中被调用时,我检查卡片的Image.Id是否存在于选定的图像列表中,并将其设置为选中状态。所以最后,这是我为LoadImages()编写的伪代码:

private async Task LoadImages()
{
    // Call Database Service to fetch current page's Image entities  

    // Updates the page with the new image list 
    await InvokeAsync(StateHasChanged); 

    // Trigger event to refresh ImagesContainer´s ImageCard child components in order to set checked state
    ManagerService.RefreshImages();
}

问题发生在StateHasChanged调用之后,它似乎以同步的“fire and forget”方式运行(我假设这是因为StateHasChanged返回void而不是Task),因为OnRefreshImages事件在页面重新呈现之前被触发,并且在这一点上,ImagesContainer仍然保存着分页之前的旧ImageCard组件。
在两个调用之间插入一个await Task.Delay(1000)解决了这个问题,现在在重新填充的容器上调用了OnRefreshImages,但是具有任意的硬编码延迟并不是很漂亮,这是一个糟糕的用户体验,因为延迟的状态更新。
我是否应该在调用数据库服务后启动Image模型中的IsSelected属性?这是否会阻止在StateHasChanged期间自动生成OnRefreshImages事件并更新ImageCard组件的需要?
编辑:关于包含ImagesContainerGallery页面标记的更多上下文:

@if (_projects == null)
{
    <LoadingSpinner />
}
else if (_projects.Count() == 0 && ManagerService.State.Gallery.FolderId == 0)
{
    <CreateProjectForm />
}
else
{
    if (images != null && images.Images != null && images.Images.Count > 0)
    {
        <ImagesContainer @ref=_imagesContainer Images="@images" />
    }
    else if (images.Images.Count == 0)
    {
        <MudText Typo="Typo.h5">Empty Project</MudText>
    }
    else
    {
        <LoadingSpinner />
    }
}

应用程序使用“Projects”作为文件夹来保持图像的组织。这里我展示了一个微调器,同时_projects变量被OnAfterRenderAsync覆盖填充(不记得为什么我选择这个而不是OnInitializedAsync,但也许这就是问题所在)。从数据库中获取项目后,我调用LoadImages()来填充相关的项目图像。
StateHasChanged调用主要用于在初始化后重新呈现页面,因为由于某种原因,在从数据库调用初始化_projects变量后,页面不会刷新,并且它会卡住,显示加载微调器,好像_projects仍然为null。
ManagerService.RefreshImages()目的只是一个触发器,让管理器在ImageContainer更新后,通过加载新的分页页面或其他交互来传播OnRefreshImages的事件以刷新ImageCard组件。这是实现:
public void RefreshImages() => OnRefreshImages?.Invoke();

umuewwlo

umuewwlo1#

在没有查看实际代码的情况下,我只是做了一些有根据的猜测。
您应该只需要调用StateHasChanged来:
1.如果要在Ui事件运行时显示状态,请在UI事件期间更新UI。
1.在事件处理程序中,如上面所述。
在几乎所有其他情况下,对StateHasChanged的调用都会强制更新UI,以掩盖逻辑问题(这些问题通常会在以后困扰您!)
“由于我设置的一些标记条件(在页面加载和其他情况下显示微调器等),我必须依赖await InvokeAsync(StateHasChanged)在此之后更新页面。”
听起来你的逻辑可能是错误的,除非上面的1适用。
调用StateHasChanged不会呈现组件:它将一个RenderFragment在渲染器的队列中排队。渲染器只有在获得线程时间时才能为它的队列提供服务。
LoadImages是一个同步代码块, Package 在一个Task中。StateHasChanged是一个同步函数--它只是加载队列。ManagerService.RefreshImages();看起来像一个同步方法,所以渲染器只在LoadImages完成后获得线程时间。添加await Task.Delay通过产生并给予渲染器线程时间来解决这个问题。您只需要一个Delay(1)await Task.Yield()也可以工作。

相关问题