本文讲解如何在 Blazor 中正确复用自定义组件(如 TextArea),通过 @bind 双向绑定机制实现多个实例间状态隔离,并在父组件中分别获取各自独立的输入值。
本文讲解如何在 blazor 中正确复用自定义组件(如 textarea),通过 `@bind` 双向绑定机制实现多个实例间状态隔离,并在父组件中分别获取各自独立的输入值。
在 Blazor 开发中,经常需要在同一页面多次使用相同的功能性组件(例如多个富文本编辑区、输入框或表单字段)。但若直接重复渲染未设计为可绑定的组件(如原始示例中无参数交互的 <TextArea/>),所有实例会共享同一份内部状态(如 result 字段),导致数据互相覆盖,无法区分各实例的用户输入。
要解决这一问题,核心是让子组件支持可绑定属性(bindable parameter)——即遵循 Blazor 的双向绑定约定:提供一个 [Parameter] public T Value { get; set; } 和一个对应的 [Parameter] public EventCallback<T> ValueChanged { get; set; }。这样,父组件可通过 @bind-Value 语法与每个子组件建立独立的数据通道。
以下是改造后的 TextArea.razor 组件完整实现:
@inject IJSRuntime JsRuntime@inject NavigationManager NavManager<table width="100%"> <tr> <td> <table width="100%"> <tr> <td> <button onclick="makeBold()">put bold</button> </td> </tr> </table> </td> </tr> <tr> <td> <textarea class="custom-textarea" id="[email protected]()" @bind="result" placeholder="Deutsche Beschreibung" rows="5"></textarea> </td> </tr></table><button @onclick="FinishAsync">finish</button>@code { [Parameter] public required string Value { get; set; } [Parameter] public required EventCallback<string> ValueChanged { get; set; } private string result = ""; protected override void OnInitialized() { // 初始化时同步外部传入的 Value 到内部 result,确保首次渲染显示正确值 result = Value; } private async Task FinishAsync() { // 触发回调,将当前 result 值通知父组件 await ValueChanged.InvokeAsync(result); } // 当内部 result 变化时(如用户输入),也应同步更新绑定值(可选,但推荐) private async Task OnResultChanged(string newValue) { result = newValue; await ValueChanged.InvokeAsync(result); }}
✅ 关键改进说明:
- 使用 [Parameter] public required string Value 和 EventCallback<string> ValueChanged 构成标准绑定契约;
- OnInitialized() 中初始化 result,避免首次渲染丢失初始值;
- id 属性添加唯一标识(如 @Guid.NewGuid()),防止多个 <textarea> 共享同一 ID 引发 DOM 冲突(尤其在 JS 互操作中);
- FinishAsync() 显式触发 ValueChanged,供父组件响应“完成”动作;你也可选择监听 @bind 的实时变化(通过 @oninput 或 @bind:after),但显式提交更符合语义。
在父组件中,只需为每个 <TextArea> 实例声明独立的绑定字段,并使用 @bind-Value 语法:
<TextArea @bind-Value="Value1" /><TextArea @bind-Value="Value2" /><TextArea @bind-Value="Value3" />@code { private string Value1 { get; set; } = string.Empty; private string Value2 { get; set; } = string.Empty; private string Value3 { get; set; } = string.Empty; // 如需在任意时刻读取全部值(例如点击“提交”按钮),可直接访问这些字段 private void HandleSubmit() { Console.WriteLine($"Value1: '{Value1}', Value2: '{Value2}', Value3: '{Value3}'"); // 进行业务处理... }}
⚠️ 注意事项:
通过上述方式,你不仅能安全复用组件,还能清晰掌控每个实例的数据生命周期——真正实现“一次编写,多处独立使用”。