Blazor StateHasChanged() 不更新子组件

Nei*_*l W 10 asp.net-core blazor blazor-webassembly

我有一项包含项目目录的服务。这些项目取决于服务的活跃公司。

// 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;
Run Code Online (Sandbox Code Playgroud)

上面的方法单独运行效果很好。目录将更新为来自相应公司的项目。

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

await Service.SetActiveCompany(dropdown.selectedCompanyId);
Run Code Online (Sandbox Code Playgroud)

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

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

我预计这会导致 CatalogSectionA 和 CatalogSectionB 组件刷新。

目录A部分

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

目录B部分

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

我原本期望在父组件中调用 StateHasChanged() 来强制重新渲染 CatalogSectionA 和 CatalogSectionB,但事实并非如此。

我可以在每个方法上放置一个 Refresh 方法,并在处理公司更改事件时从父级调用这些方法,但我认为 Blazor 没有必要这样做,并且 StateHasChanged() 可以解决这个问题吗?

Nei*_*l W 12

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

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

因此,我的结论是,如果我在父组件上调用 StateHasChanged(),则只有当 Blazor 检测到子组件中对父组件中的更改存在“依赖性”时,子组件才会重新渲染。

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

  • 是的,你是对的。设置参数的值会触发子级上的“SetParametersAsync”,并且对“StateHasChanged”的调用是该过程的一部分。有一种替代方法取决于您的服务范围。如果它是有作用域的,则每个需要了解“CompanyChanged”事件的组件都会注入服务(它们都是相同的对象实例)注册该事件,然后在事件处理程序中调用“StateHasChanged”。 (3认同)

Paw*_*wel 7

在子组件中:

[Parameter] public Func<bool>? ShouldReRender { get; set; }
Run Code Online (Sandbox Code Playgroud)

在父级中:

<MyComponent ShouldReRender="ShouldRender" />
Run Code Online (Sandbox Code Playgroud)

这将创建所需的依赖关系,并且 imo 比虚拟 CascadingParameter 或 RenderFragment 更清晰

  • 这是一个很好且简单的解决方案,谢谢 (2认同)