Blazor StateHasChanged()未更新子组件



我有一个包含项目目录的服务。项目取决于服务的活动公司。

// Tell Service to refresh in context of Company 1
await Service.SetActiveCompany(1);
// Get Catalog (which will be in the context of Company 1)
Catalog catalog = Service.Catalog;
// Tell Service to refresh in context of Company 2
await Service.SetActiveCompany(2);
// Get Catalog (which will be in the context of Company 2)
catalog = Service.Catalog;

以上内容单独使用效果良好。目录将使用相应公司的项目进行刷新。

在我的应用程序中,我有一个布局,其中包括用于选择活动公司的下拉列表。当这种情况发生变化时,我会呼叫:

await Service.SetActiveCompany(dropdown.selectedCompanyId);

在服务中,我刷新目录并引发事件

public class CompanyChangedEventArgs : EventArgs { }
public class Service
{
public event EventHandle<CompanyChangedEventArgs> CompanyChanged;
protected void OnCompanyChanged() 
=> CompanyChanged?.Invoke(this, new CompanyChangedEventArgs());
public Catalog Catalog { get; private set; }
public async Task SetActiveCompany(int companyId)
{
Catalog = await API.GetCatalogForCompany(companyId);
OnCompanyChanged();
}
}

在父组件中(使用上面的布局(,我捕获事件

<CatalogSectionA />
<CatalogSectionB />
@code {
protected override void OnInitialized()
{
Service.CompanyChanged += HandleCompanyChanged;
base.OnInitialized();
}
HandleCompanyChanged(object sender, EventArgs e) => StateHasChanged();
}

我希望这会导致CatalogSectionA和CatalogSectionB组件刷新。

CatalogSectionA

<div>
@foreach (Item i in Catalog.SectionA.Items)
{
<div>@i.Name</div>
}
</div>
@code {
Catalog Catalog => Service.Catalog;
}

CatalogSectionB

<div>
@foreach (Item i in Catalog.SectionB.Items)
{
<div>@i.Name</div>
}
</div>
@code {
Catalog Catalog => Service.Catalog;
}

我曾期望在父组件中调用StateHasChanged((来强制重新呈现CatalogSectionA和CatalogSectionB,但事实并非如此。

我可以在每一个上放一个Refresh方法,并在处理公司变更事件时从父级调用这些方法,但我认为Blazor和StateHasChanged((不需要这样做?

在子组件上放置一个"冗余"级联参数后,它就可以工作了。

根本没有传递任何参数,因为状态(参数(是在服务中管理的,我希望StateHasChanged((能提示子组件重新呈现并返回服务。

因此,我的结论是,如果我对父组件调用StateHasChanged((,那么只有当Blazor在子组件中检测到对父组件中的更改的"依赖性"时,子组件才会被重新呈现。

如果没有依赖项(即没有参数或级联参数(,那么看起来Blazor假设当父组件的状态发生变化时,子组件将不需要任何更改。

在子组件中:

[Parameter] public Func<bool>? ShouldReRender { get; set; }

在父级:

<MyComponent ShouldReRender="ShouldRender" />

这将创建所需的依赖项,并且imo比伪CascadingParameter或RenderFragment 更清晰

所以我刚才遇到了这个问题,我认为可能值得添加一个替代解决方案。

我的用例涉及Blazor MAUI应用程序中的mDNS和BLE 5.3+,具有持续的轮询和频繁但零星的更新场景。

起初,我使用计时器定期更新组件,但这相当不雅。然后我转到Pawel概述的解决方案,但我的最终解决方案是创建一个名为GlobalRenderManager的单例服务。

结构相当简单:

internal sealed class GlobalRenderManager
{
internal event EventHandler<int>? ForceRender;
internal void _Render(int _CID)
{
ForceRender?.Invoke(this, _CID);
}
}

将其作为单例注入允许组件订阅事件处理程序,并允许我从代码中的任何其他位置调用单个组件呈现。

单个组件可以订阅事件,并在事件触发特定整数时调用其内部StateHasChanged((。

它可能仍然不是一个完美的解决方案,但它确实允许对需要更频繁的基于事件的更新的应用程序进行更灵活的控制。

相关内容

  • 没有找到相关文章

最新更新