Blazor辅助类调用组件方法未触发重渲染问题咨询
Blazor模态框通过服务调用不显示的问题及重渲染机制解析
问题场景
当直接从父组件通过@ref调用ModalDialog的display方法时,模态框能正常显示:
ModalDialog组件代码
ModalDialog.razor
@if (isDisplaying) { <div class=@($"ModalDialog--YDF { rootElementModifierCSS_Classes }") role="dialog" tabindex="-1" > <div class="ModalDialog--YDF-Body" role="document"> <!-- ... --> @ChildContent </div> </div> }
ModalDialog.razor.cs
using YamatoDaiwa.CSharpExtensions; using Utils; namespace FrontEndFramework.Components.ModalDialog; public partial class ModalDialog : Microsoft.AspNetCore.Components.ComponentBase { /* ━━━ 参数 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */ [Microsoft.AspNetCore.Components.Parameter] public string? title { get; set; } [Microsoft.AspNetCore.Components.Parameter] public required bool isInitiallyDisplaying { get; set; } = false; [Microsoft.AspNetCore.Components.Parameter] public bool noDismissingButton { get; set; } = false; [Microsoft.AspNetCore.Components.Parameter] public required Microsoft.AspNetCore.Components.EventCallback<ModalDialog> onInitializedEventCallback { get; set; } [Microsoft.AspNetCore.Components.Parameter] public required Microsoft.AspNetCore.Components.RenderFragment ChildContent { get; set; } /* ━━━ 状态 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */ private bool isDisplaying = false; /* ━━━ 公开方法 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */ public void display() { this.isDisplaying = true; } public void dismiss() { this.isDisplaying = false; } /* ━━━ 生命周期钩子 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */ protected override void OnInitialized() { base.OnInitialized(); this.isDisplaying = this.isInitiallyDisplaying; this.onInitializedEventCallback.InvokeAsync(this); } }
父组件调用代码
父组件.razor
<ModalDialog title=@localization.filteringModalDialog.title @ref=tasksFilteringModalDialog > <TasksFilteringPanel /> </ModalDialog> <button type="button" @onclick=@displayTasksFilteringModalDialog>open</button>
父组件.razor.cs
public partial class TasksManagementPageContent : Microsoft.AspNetCore.Components.ComponentBase { private ModalDialog tasksFilteringModalDialog = null!; private void displayTasksFilteringModalDialog() { this.tasksFilteringModalDialog.display(); } // ... }
但当按钮和模态框不是同级组件时,我通过一个静态服务类来调用:
服务类代码
public class TasksFilteringModalDialogService { protected static ModalDialog? modalDialogInstance = null; public static void initialize(ModalDialog modalDialogInstance) { TasksFilteringModalDialogService.modalDialogInstance = modalDialogInstance; } public static void displayModalDialog() { TasksFilteringModalDialogService.modalDialogInstance?.display(); } public static void dismissModalDialog() { TasksFilteringModalDialogService.modalDialogInstance?.dismiss(); } }
组件中使用服务
<ModalDialog title=@localization.filteringModalDialog.title onInitializedEventCallback=@TasksFilteringModalDialogService.initialize > <TasksFilteringPanel /> </ModalDialog>
确认所有方法都已调用,但模态框不显示,只有在ModalDialog.display()中手动调用StateHasChanged()才生效,这不符合预期。
Blazor重渲染触发条件及问题原因
Blazor自动触发重渲染的场景
Blazor组件会在以下情况自动触发重渲染:
- 组件的**参数(Parameter)**发生变化(Blazor会对比参数的旧值和新值)
- 组件内部通过EventCallback触发的事件处理方法执行完成后
- 组件使用的**绑定属性(@bind)**更新时
- 调用组件的**StateHasChanged()**方法(手动触发)
问题原因分析
父组件直接调用的场景:
当父组件通过@ref调用modalDialog.display()时,这个调用是在父组件的displayTasksFilteringModalDialog方法中执行的,而这个方法是通过按钮的@onclick绑定的EventCallback触发的。EventCallback执行完成后,Blazor会自动触发父组件的重渲染,父组件重渲染时会连带更新其子组件(包括ModalDialog),此时ModalDialog的isDisplaying已经被设为true,所以能正常显示。静态服务调用的场景:
当通过静态服务调用modalDialog.display()时,这个操作脱离了Blazor组件生命周期的上下文:- 服务的静态方法直接修改ModalDialog的
isDisplaying状态,但这个操作没有触发Blazor的重渲染机制 - 因为修改状态的代码不在EventCallback、参数更新等自动触发重渲染的场景里,Blazor无法感知到ModalDialog的状态已经变化,所以不会自动重渲染组件
- 只有手动调用
StateHasChanged(),才能告诉Blazor组件状态已更新,需要重新渲染UI
- 服务的静态方法直接修改ModalDialog的
内容的提问来源于stack exchange,提问作者Takeshi Tokugawa YD
相关产品推荐
相关产品推荐

