Blazor子组件属性渲染异常:父页面首次加载无法获取正确值
问题:Blazor父页面首次渲染无法获取子组件正确属性值
编写Blazor封装组件时,遇到父页面首次渲染无法正确读取子组件属性值的问题,简化示例如下:
子组件(RBTest)
<div class="col-2"> <input @bind="TotalRows"></input> Total Rows</div> <div class="col-2"> @TotalRows Total Rows</div> <div class="col-2"> @GetRows() Total Rows</div> @code { public DataTable dtFull = new DataTable(); public int TotalRows { get; set; } = 0; public RBTest() { } public RBTest(RBDataTable i_RBDT) { dtFull = i_RBDT.ReadAllDetails(); //数据访问层方法 TotalRows = dtFull.Rows.Count; } public int GetRows() { return TotalRows; } }
父页面
<PageTitle>Index</PageTitle> <RBTest @ref="Test"></RBTest> <div>@Test.GetRows()</div> @code { RBTest Test = new RBTest(); //Blazor组件实例 T_DIV_DIVIDEND Data = new T_DIV_DIVIDEND(); //数据访问层对象,暂忽略 protected override async Task OnInitializedAsync() { Test = new RBTest(Data.RBDT); } }
现象
子组件内部的TotalRows值显示正确,但父页面首次渲染时调用Test.GetRows()返回0。
原因
核心问题是手动实例化组件与Blazor框架管理的组件实例不一致:
- 父页面中
@ref="Test"绑定的是Blazor框架渲染时创建的子组件实例,而非手动new的实例。 - 首次渲染时,父页面使用的是初始
new RBTest()的实例(TotalRows为0),而OnInitializedAsync中重新赋值的Test实例并未关联到UI上的子组件,框架也不会渲染这个手动创建的实例。
解决方案
1. 遵循Blazor组件规范:用参数传递数据,弃用自定义构造函数
Blazor组件不推荐通过自定义构造函数接收数据,标准方式是通过[Parameter]属性传递参数,并在生命周期方法中初始化数据。
修改后的子组件:
<div class="col-2"> <input @bind="TotalRows"></input> Total Rows</div> <div class="col-2"> @TotalRows Total Rows</div> <div class="col-2"> @GetRows() Total Rows</div> @code { private DataTable dtFull = new DataTable(); public int TotalRows { get; set; } = 0; // 用参数接收外部传入的数据 [Parameter] public RBDataTable RBDT { get; set; } // 参数更新时初始化数据 protected override void OnParametersSet() { if (RBDT != null) { dtFull = RBDT.ReadAllDetails(); TotalRows = dtFull.Rows.Count; } } public int GetRows() { return TotalRows; } }
2. 父页面正确管理组件实例,依赖框架赋值@ref
父页面不再手动创建组件实例,由Blazor框架通过@ref赋值,并确保在组件渲染完成后再访问实例。
修改后的父页面:
<PageTitle>Index</PageTitle> <!-- 通过参数传递数据给子组件 --> <RBTest @ref="Test" RBDT="Data.RBDT"></RBTest> <!-- 空引用保护,避免首次渲染时Test未赋值导致报错 --> <div>@(Test?.GetRows() ?? 0)</div> @code { // 仅声明变量,由框架赋值 private RBTest Test; private T_DIV_DIVIDEND Data = new T_DIV_DIVIDEND(); protected override async Task OnInitializedAsync() { // 如果Data.RBDT需要异步加载,在此完成初始化 // await Data.LoadRBDTAsync(); // 异步操作后通知框架重新渲染 StateHasChanged(); } // 首次渲染完成后,安全访问子组件实例 protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { // 此时Test已被框架赋值,可以正常调用方法 var rowCount = Test.GetRows(); // 执行后续逻辑 } } }
关键修改点
- 子组件移除自定义构造函数,改用
[Parameter]接收数据,在OnParametersSet中初始化属性,符合Blazor生命周期规范。 - 父页面不再手动实例化组件,
@ref由框架在组件渲染完成后赋值。 - 使用
Test?.GetRows() ?? 0避免首次渲染时的空引用异常。 - 如需在首次渲染后操作子组件,在
OnAfterRenderAsync的firstRender分支中处理,确保组件实例已就绪。
内容的提问来源于stack exchange,提问作者Richard Barker
相关产品推荐
相关产品推荐

