Blazor父组件误触发子组件、首次点击无效问题解决咨询
解决Blazor DynamicComponent仅在按钮点击时触发的问题
核心思路
通过隔离查询参数、控制渲染触发条件,让子组件只在按钮点击且表单验证通过时才执行API调用和渲染,避免不必要的重渲染和异步冲突。
具体修改步骤
1. 父组件调整
- 新增两个变量:一个布尔值控制表格是否渲染,一个独立的查询参数对象存储提交时的表单数据(避免直接绑定原表单对象导致频繁变更)
- 点击提交按钮时,先完成表单验证,通过后再复制表单数据到查询参数,触发子组件更新
@page "/" @using System.ComponentModel.DataAnnotations <EditForm Model="@SearchModel" OnValidSubmit="HandleSubmit"> <DataAnnotationsValidator /> <ValidationSummary /> <input type="text" @bind="SearchModel.Keyword" placeholder="关键词" /> <input type="number" @bind="SearchModel.PageSize" placeholder="每页数量" /> <button type="submit">查询</button> </EditForm> @if (ShouldRenderTable) { <DynamicComponent Type="typeof(TableComponent)" Parameters='new Dictionary<string, object> { { "QueryParams", CurrentQueryParams } }' /> } @code { private SearchModel SearchModel = new(); private SearchModel? CurrentQueryParams; private bool ShouldRenderTable; private void HandleSubmit() { // 复制表单数据到独立对象,避免原表单输入触发参数变更 CurrentQueryParams = new SearchModel { Keyword = SearchModel.Keyword, PageSize = SearchModel.PageSize }; ShouldRenderTable = true; } public class SearchModel { [Required] public string Keyword { get; set; } = string.Empty; [Range(1, 100)] public int PageSize { get; set; } = 10; } }
2. 子组件调整
- 用
[Parameter]接收查询参数,重写OnParametersSetAsync,仅当参数真正变化时调用API - 加入异步操作取消机制,避免多次点击导致的并发错误
@if (DataList == null) { <p>加载中...</p> } else { <table> <thead> <tr> <th>ID</th> <th>名称</th> </tr> </thead> <tbody> @foreach (var item in DataList) { <tr> <td>@item.Id</td> <td>@item.Name</td> </tr> } </tbody> </table> } @code { [Parameter] public SearchModel? QueryParams { get; set; } private List<DataItem>? DataList; private CancellationTokenSource? _cts; private SearchModel? _lastQueryParams; protected override async Task OnParametersSetAsync() { // 仅当查询参数真正变化时才执行API调用 if (QueryParams != null && !EqualityComparer<SearchModel>.Default.Equals(QueryParams, _lastQueryParams)) { _lastQueryParams = QueryParams; // 取消之前的异步操作,避免并发冲突 _cts?.Cancel(); _cts = new CancellationTokenSource(); try { DataList = await GetDataAsync(QueryParams, _cts.Token); } catch (OperationCanceledException) { // 忽略取消操作的异常 } catch (Exception ex) { // 处理其他异常 Console.WriteLine(ex.Message); } } } private async Task<List<DataItem>> GetDataAsync(SearchModel queryParams, CancellationToken token) { // 模拟API调用 await Task.Delay(500, token); return new List<DataItem> { new DataItem { Id = 1, Name = $"{queryParams.Keyword}_数据1" }, new DataItem { Id = 2, Name = $"{queryParams.Keyword}_数据2" } }; } public class DataItem { public int Id { get; set; } public string Name { get; set; } = string.Empty; } public class SearchModel { public string Keyword { get; set; } = string.Empty; public int PageSize { get; set; } } public override void Dispose() { _cts?.Dispose(); base.Dispose(); } }
问题解决说明
- 首次点击GetData不触发:现在通过独立的
CurrentQueryParams传递参数,首次点击时参数从null变为有效对象,OnParametersSetAsync会检测到变化并触发API调用。 - 父组件变更触发子组件:原表单对象的日常输入不会影响
CurrentQueryParams,只有点击提交按钮时才更新该参数,避免不必要的重渲染。 - 多次点击报错:加入了
CancellationTokenSource,每次点击会取消之前未完成的API请求,避免并发操作导致的冲突和异常。
内容的提问来源于stack exchange,提问作者causita
相关产品推荐
相关产品推荐

