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

Blazor如何访问重复渲染Razor组件的taskList参数

问题根因

你现在访问不到复用Todo组件内部taskList的本质原因是Blazor的组件隔离设计:

  • 你写在Todo组件里的taskList是protected级别的内部字段,没有标记[Parameter]特性,父组件无法直接通过组件传参访问
  • 即便你用@ref获取子组件实例直接读这个字段,也属于反模式:子组件内部修改列表时父组件不会收到变更通知,UI不会自动同步,后续非常容易出状态不一致的bug。

Blazor推荐的做法是状态提升:把需要跨组件访问的状态放到共同的父组件持有,子组件只负责接收数据、上报用户操作,由父组件统一修改状态。


实现步骤

1. 改造Todo子组件

把原来组件内部持有的taskList改成接收父组件传参,所有对列表的修改操作通过EventCallback通知父组件执行,顺便修复你原来代码里点击切换任务完成状态的逻辑bug(原来写的taskitem.IsComplete = task.IsComplete是错误逻辑,会导致状态异常)。

改造后的Todo组件代码:

<h3>@Headername</h3>

<EditForm Model="task" class="form-inline" OnValidSubmit="HandleAddTask">
    <input type="text" class="form-control mx-2" @bind-value="task.TaskName" />
    <button class="btn btn-outline-primary mx-2">Add Task</button>
</EditForm>

<div class="progress-bar my-2" role="progressbar" style="width:@(PercentageCompleted)%" aria-valuemax="100" aria-valuemin="0">
    @(PercentageCompleted)%
</div>

<ul class="list-group my-2">
    @foreach (var taskitem in TaskList)
    {
        if (taskitem.IsComplete)
        {
            <li class="list-group-item list-group-item-success mx-1" @onclick="() => ToggleTask(taskitem)"> <del>@taskitem.TaskName</del></li>
        }
        else
        {
            <li class="list-group-item list-group-item-secondary mx-1" @onclick="() => ToggleTask(taskitem)">@taskitem.TaskName</li>
        }
    }
</ul>

@code {
    [Parameter]
    public string Headername { get; set; } = "Todo";

    // 接收父组件传入的任务列表
    [Parameter]
    public List<TaskModel> TaskList { get; set; } = new();

    // 新增任务事件,通知父组件往列表加数据
    [Parameter]
    public EventCallback<TaskModel> OnTaskAdded { get; set; }

    // 切换任务状态事件,通知父组件修改对应任务的完成状态
    [Parameter]
    public EventCallback<TaskModel> OnTaskToggled { get; set; }

    private TaskModel task = new TaskModel();

    private int PercentageCompleted {
        get {
            return TaskList.Count > 0 ? (TaskList.Count(x => x.IsComplete) * 100 / TaskList.Count) : 0;
        }
    }

    private async Task HandleAddTask()
    {
        await OnTaskAdded.InvokeAsync(task);
        task = new TaskModel();
    }

    private async Task ToggleTask(TaskModel taskItem)
    {
        taskItem.IsComplete = !taskItem.IsComplete;
        await OnTaskToggled.InvokeAsync(taskItem);
    }
}

注意:把Todo组件顶部多余的@page "/Tests"去掉,可复用组件不需要加路由指令,只有独立页面才需要。

2. 改造ToDos父页面

在父页面持有两个独立的任务列表,传给对应的Todo组件,处理子组件的事件更新列表,页面底部可以直接读取两个列表的内容输出:

@page "/Todo"

<div class="row">
    <div class="col-6">
        <Todo Headername="Daily Task" 
             TaskList="dailyTasks" 
             OnTaskAdded="AddDailyTask"
             OnTaskToggled="() => StateHasChanged()">
        </Todo>
    </div>
    <div class="col-6">
        <Todo Headername="Monthly Task" 
             TaskList="monthlyTasks"
             OnTaskAdded="AddMonthlyTask"
             OnTaskToggled="() => StateHasChanged()">
        </Todo>
    </div>
</div>

<div class="mt-4">
    <h4>所有任务汇总</h4>
    <div class="row">
        <div class="col-6">
            <h5>Daily Task 列表(共@dailyTasks.Count项,已完成@dailyTasks.Count(x=>x.IsComplete)项)</h5>
            <ul>
                @foreach (var item in dailyTasks)
                {
                    <li>@item.TaskName - @(item.IsComplete ? "已完成" : "未完成")</li>
                }
            </ul>
        </div>
        <div class="col-6">
            <h5>Monthly Task 列表(共@monthlyTasks.Count项,已完成@monthlyTasks.Count(x=>x.IsComplete)项)</h5>
            <ul>
                @foreach (var item in monthlyTasks)
                {
                    <li>@item.TaskName - @(item.IsComplete ? "已完成" : "未完成")</li>
                }
            </ul>
        </div>
    </div>
</div>

@code {
    // 父组件统一持有两个列表的状态
    private List<TaskModel> dailyTasks = new();
    private List<TaskModel> monthlyTasks = new();

    private void AddDailyTask(TaskModel newTask)
    {
        dailyTasks.Add(newTask);
    }

    private void AddMonthlyTask(TaskModel newTask)
    {
        monthlyTasks.Add(newTask);
    }
}

不推荐的方式(仅作参考)

如果只是临时测试想直接拿子组件内部的taskList,可以给Todo组件加@ref获取组件实例,直接访问修饰符改为public的taskList字段,但这种方式子组件内部更新列表时父组件不会自动刷新,必须手动触发StateHasChanged,实际项目不要这么写:

<Todo @ref="dailyTodoRef" Headername="Daily Task"></Todo>
<Todo @ref="monthlyTodoRef" Headername="Monthly Task"></Todo>

@code {
    private Todo dailyTodoRef;
    private Todo monthlyTodoRef;
    // 访问方式:dailyTodoRef.taskList,注意要先把Todo组件里taskList的访问修饰符从protected改成public
}

内容的提问来源于stack exchange,提问作者GRAGW

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 00:01:24