Blazor Server调用子组件方法触发空引用异常问题求助
问题描述
我有一个Blazor页面,路由配置如下:
@page "/master/{Id:int}" @page "/master/{Id:int}/todo/{TodoId:int}"
页面的某个标签页中引入了子组件:
<CompTodo ParentId="@Id.Value" @ref="compTodo" />
代码部分:
CompTodo compTodo; // ... 其他代码 protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { if (TodoId != null) { TabsSelectedIndex = 3; // 切换到待办事项视图标签页 await compTodo.TaskEdit(TodoId.Value); } } }
compTodo.TaskEdit()用于在CompTodo组件中打开编辑弹窗。主页面中用户的5条待办事项列表每行按钮调用该方法可正常工作。
但我的站点另有一个展示用户所有待办事项的页面,需要从该页面调用此方法。
在firstRender中调用await compTodo.TaskEdit()会抛出"System.NullReferenceException",推测是CompTodo组件未完全加载;改用!firstRender时弹窗可显示,但会陷入无限循环。
我尝试添加参数int RenderCount,并在OnAfterRenderAsync中处理:
{ if (firstRender) { RenderCount = 0; } if (RenderCount == 1) { // 调用我的方法 } RenderCount++; }
但仍会无限调用组件方法。
请问如何在所有组件加载完成后,仅调用一次子组件的方法?
解决方案
方法1:状态标记+路由参数监听
添加布尔标记控制方法执行次数,同时监听路由参数变化,确保仅在首次加载且参数有效时执行一次:
CompTodo compTodo; private bool _hasRunTaskEdit = false; private int? _lastTodoId; protected override async Task OnAfterRenderAsync(bool firstRender) { // 满足TodoId存在、方法未执行、子组件实例已初始化三个条件才执行 if (TodoId != null && !_hasRunTaskEdit && compTodo != null) { TabsSelectedIndex = 3; await compTodo.TaskEdit(TodoId.Value); _hasRunTaskEdit = true; } } // 路由参数变化时重置标记,支持从其他页面跳转过来的新参数 protected override void OnParametersSet() { if (TodoId != _lastTodoId) { _hasRunTaskEdit = false; _lastTodoId = TodoId; } }
方法2:子组件内部处理触发逻辑
把弹窗触发逻辑移到子组件中,通过参数传递待编辑的TodoId,子组件自身监听参数变化执行操作,更符合Blazor组件设计逻辑:
- 主页面修改组件引用:
<CompTodo ParentId="@Id.Value" TargetTodoId="@TodoId" />
- 子组件
CompTodo添加参数并处理:
[Parameter] public int? TargetTodoId { get; set; } private int? _prevTargetTodoId; protected override async Task OnParametersSetAsync() { // 仅当参数变化且不为空时打开弹窗 if (TargetTodoId != null && TargetTodoId != _prevTargetTodoId) { await TaskEdit(TargetTodoId.Value); _prevTargetTodoId = TargetTodoId; } }
方法3:确保组件就绪后执行
通过标记位确认组件完全加载,避免空引用同时防止无限循环:
CompTodo compTodo; private bool _isComponentReady = false; private bool _hasExecuted = false; protected override void OnAfterRender(bool firstRender) { if (firstRender) { _isComponentReady = true; StateHasChanged(); // 触发二次渲染,确保子组件已初始化 } } protected override async Task OnAfterRenderAsync(bool firstRender) { if (_isComponentReady && !_hasExecuted && TodoId != null && compTodo != null) { TabsSelectedIndex = 3; await compTodo.TaskEdit(TodoId.Value); _hasExecuted = true; } }
内容的提问来源于stack exchange,提问作者Tobias Gustafsson
相关产品推荐
相关产品推荐

