You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 16:04:55