Blazor 8中是否存在React useFormContext的等效实现?
类似React Hook Form中useFormContext的Blazor实现方案
Blazor原生并没有直接对应useFormContext的API,但可以通过**级联值(CascadingValue)**结合EditForm自定义实现完全等效的功能,彻底避免手动传递表单参数,支持原子化组件开发。以下是具体实现步骤和示例:
核心思路
通过封装一个包含EditContext和表单状态的上下文类,再用CascadingValue将上下文自动传递给所有子组件,让原子化输入组件无需手动接收表单参数即可获取表单值、验证错误等信息。
1. 创建表单上下文类
封装表单的核心状态和常用方法,方便子组件统一调用:
public class FormContext { public EditContext EditContext { get; } public ValidationMessageStore ValidationMessages { get; } public FormContext(EditContext editContext) { EditContext = editContext; ValidationMessages = new ValidationMessageStore(editContext); } // 获取指定字段的错误信息 public IEnumerable<string> GetFieldErrors(string fieldName) { var fieldId = new FieldIdentifier(EditContext.Model, fieldName); return ValidationMessages.Get(fieldId); } // 手动触发字段验证 public void ValidateField(string fieldName) { var fieldId = new FieldIdentifier(EditContext.Model, fieldName); EditContext.ValidateField(fieldId); } }
2. 封装带级联上下文的表单组件
创建自定义表单组件,内部包裹EditForm并注入FormContext:
@typeparam TModel @inherits ComponentBase <CascadingValue Value="_formContext" IsFixed="true"> <EditForm Model="@Model" OnValidSubmit="@OnValidSubmit" OnInvalidSubmit="@OnInvalidSubmit" Context="editContext"> @ChildContent </EditForm> </CascadingValue> @code { [Parameter] public TModel Model { get; set; } = default!; [Parameter] public EventCallback<EditContext> OnValidSubmit { get; set; } [Parameter] public EventCallback<EditContext> OnInvalidSubmit { get; set; } [Parameter] public RenderFragment? ChildContent { get; set; } private FormContext? _formContext; protected override void OnParametersSet() { base.OnParametersSet(); if (Model != null) { var editContext = new EditContext(Model); _formContext = new FormContext(editContext); } } }
3. 编写原子化输入组件
子组件通过级联参数自动获取FormContext,无需手动传递表单相关参数:
@typeparam TValue @inherits ComponentBase @if (_formContext == null) { throw new InvalidOperationException("输入组件必须在FormWithContext内使用"); } <div class="form-group"> <label>@Label</label> <InputText @bind-Value="@Value" @bind-Value:event="oninput" EditContext="@_formContext.EditContext" class="form-control @GetInputClass()" /> @foreach (var error in _formContext.GetFieldErrors(FieldName)) { <div class="text-danger">@error</div> } </div> @code { [CascadingParameter] private FormContext? _formContext { get; set; } [Parameter] public string Label { get; set; } = string.Empty; [Parameter] public TValue Value { get; set; } = default!; [Parameter] public EventCallback<TValue> ValueChanged { get; set; } [Parameter] public string FieldName { get; set; } = string.Empty; private string GetInputClass() { var fieldId = new FieldIdentifier(_formContext!.EditContext.Model, FieldName); var fieldState = _formContext.EditContext.GetFieldState(fieldId); return fieldState.IsInvalid ? "is-invalid" : string.Empty; } }
4. 使用示例
直接组合原子化组件,无需手动传递表单参数:
@page "/user-form" <FormWithContext Model="@_user" OnValidSubmit="@HandleSubmit"> <TextInput Label="用户名" @bind-Value="@_user.Username" FieldName="Username" /> <TextInput Label="邮箱" @bind-Value="@_user.Email" FieldName="Email" /> <button type="submit" class="btn btn-primary">提交</button> </FormWithContext> @code { private UserModel _user = new(); private async Task HandleSubmit(EditContext editContext) { // 处理表单提交逻辑 Console.WriteLine("表单验证通过,开始提交"); } public class UserModel { [Required(ErrorMessage = "用户名不能为空")] public string Username { get; set; } = string.Empty; [Required(ErrorMessage = "邮箱不能为空")] [EmailAddress(ErrorMessage = "请输入有效的邮箱")] public string Email { get; set; } = string.Empty; } }
社区现成方案参考
部分社区库也内置了类似表单上下文的功能,比如:
- Blazored.FluentValidation:结合FluentValidation的验证库,可通过扩展级联值实现表单上下文共享
- 自定义表单上下文实现:不少开发者分享的封装方案,核心逻辑均基于
CascadingValue传递EditContext或封装后的上下文对象
内容的提问来源于stack exchange,提问作者Marcos Sequeira
相关产品推荐
相关产品推荐

