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

Blazor TodoApp开发求助:跨组件共享List<TodoItem>数据

Blazor TodoApp 组件数据共享方案

方案1:父组件托管数据,子组件通过参数+回调通信

这是Blazor最常规的组件通信方式,耦合度低,适合简单场景:

  • 先定义基础的TodoItem类:
public class TodoItem
{
    public int Id { get; set; }
    public string Title { get; set; } = string.Empty;
    public bool IsCompleted { get; set; }
}
  • 父组件(比如TodoLayout.razor)持有全局的List<TodoItem>,负责所有数据逻辑:
@page "/"

<h1>Todo App</h1>

<TodoSection Title="任务" Items="@pendingTasks" OnStatusToggle="ToggleTaskStatus" OnAddTask="AddNewTask" />
<TodoSection Title="已完成任务" Items="@completedTasks" OnStatusToggle="ToggleTaskStatus" />

@code {
    private List<TodoItem> allTasks = new();
    private List<TodoItem> pendingTasks => allTasks.Where(t => !t.IsCompleted).ToList();
    private List<TodoItem> completedTasks => allTasks.Where(t => t.IsCompleted).ToList();

    private void AddNewTask(string title)
    {
        if (!string.IsNullOrWhiteSpace(title))
        {
            allTasks.Add(new TodoItem { Id = allTasks.Count + 1, Title = title });
        }
    }

    private void ToggleTaskStatus(TodoItem item)
    {
        item.IsCompleted = !item.IsCompleted;
        StateHasChanged(); // 通知UI刷新
    }
}
  • 子组件TodoSection.razor负责UI渲染和触发事件:
<div class="todo-section">
    <h3>@Title</h3>
    @if (Title == "任务")
    {
        <input @bind="newTaskTitle" placeholder="输入新任务" />
        <button @onclick="() => OnAddTask?.Invoke(newTaskTitle)">添加</button>
    }
    <ul>
        @foreach (var item in Items)
        {
            <li>
                <input type="checkbox" 
                       @bind="item.IsCompleted" 
                       @bind:after="() => OnStatusToggle?.Invoke(item)" />
                @(item.IsCompleted ? $"<del>{item.Title}</del>" : item.Title)
            </li>
        }
    </ul>
</div>

@code {
    [Parameter] public string Title { get; set; } = string.Empty;
    [Parameter] public List<TodoItem> Items { get; set; } = new();
    [Parameter] public EventCallback<TodoItem> OnStatusToggle { get; set; }
    [Parameter] public EventCallback<string> OnAddTask { get; set; }

    private string newTaskTitle = string.Empty;
}

这个方案里,父组件掌控所有数据逻辑,子组件只做UI和事件触发,完全解耦,维护起来很方便。

方案2:使用状态容器实现全局数据共享

如果两个组件没有直接父子关系(比如在不同路由页面),用状态容器更合适,通过依赖注入让组件共享同一个数据实例:

  • 创建状态容器类TodoStateContainer:
public class TodoStateContainer
{
    private List<TodoItem> _todoItems = new();

    public List<TodoItem> TodoItems => _todoItems;

    // 状态变更通知事件
    public event Action? OnStateChanged;

    public void AddTask(string title)
    {
        if (!string.IsNullOrWhiteSpace(title))
        {
            _todoItems.Add(new TodoItem { Id = _todoItems.Count + 1, Title = title });
            NotifyStateChanged();
        }
    }

    public void ToggleTaskStatus(TodoItem item)
    {
        var targetItem = _todoItems.First(t => t.Id == item.Id);
        targetItem.IsCompleted = !targetItem.IsCompleted;
        NotifyStateChanged();
    }

    private void NotifyStateChanged() => OnStateChanged?.Invoke();
}
  • 在Program.cs里把状态容器注册为单例服务:
builder.Services.AddSingleton<TodoStateContainer>();
  • 「任务」组件(PendingTasks.razor)注入状态容器:
<div>
    <h3>任务</h3>
    <input @bind="newTaskTitle" placeholder="输入新任务" />
    <button @onclick="AddTask">添加</button>
    <ul>
        @foreach (var item in _state.TodoItems.Where(t => !t.IsCompleted))
        {
            <li>
                <input type="checkbox" 
                       @bind="item.IsCompleted" 
                       @bind:after="() => _state.ToggleTaskStatus(item)" />
                @item.Title
            </li>
        }
    </ul>
</div>

@code {
    [Inject] private TodoStateContainer _state { get; set; } = null!;
    private string newTaskTitle = string.Empty;

    protected override void OnInitialized()
    {
        // 订阅状态变更事件,触发UI更新
        _state.OnStateChanged += StateHasChanged;
    }

    // 组件销毁时取消订阅,避免内存泄漏
    public void Dispose()
    {
        _state.OnStateChanged -= StateHasChanged;
    }

    private void AddTask()
    {
        _state.AddTask(newTaskTitle);
        newTaskTitle = string.Empty;
    }
}
  • 「已完成任务」组件(CompletedTasks.razor)同样注入状态容器:
<div>
    <h3>已完成任务</h3>
    <ul>
        @foreach (var item in _state.TodoItems.Where(t => t.IsCompleted))
        {
            <li>
                <input type="checkbox" 
                       @bind="item.IsCompleted" 
                       @bind:after="() => _state.ToggleTaskStatus(item)" 
                       checked />
                <del>@item.Title</del>
            </li>
        }
    </ul>
</div>

@code {
    [Inject] private TodoStateContainer _state { get; set; } = null!;

    protected override void OnInitialized()
    {
        _state.OnStateChanged += StateHasChanged;
    }

    public void Dispose()
    {
        _state.OnStateChanged -= StateHasChanged;
    }
}

这个方案适合组件层级复杂或跨路由的场景,数据全局共享,状态变化时自动通知所有订阅组件刷新UI。

方案3:组件引用(不推荐,耦合度高)

如果两个组件是直接的父子关系,也可以通过组件引用直接访问父组件的List,但这种方式耦合性极强,后期维护困难,只适合临时简单场景:

  • 父组件TaskList.razor:
<CompletedTaskList @ref="_completedList" />

@code {
    public List<TodoItem> TodoItems { get; set; } = new();
    private CompletedTaskList? _completedList;

    // 添加任务逻辑...
}
  • 子组件CompletedTaskList.razor:
@code {
    [CascadingParameter] private TaskList? ParentComponent { get; set; }

    private List<TodoItem> CompletedTasks => ParentComponent?.TodoItems.Where(t => t.IsCompleted).ToList() ?? new();

    // 切换状态时直接操作ParentComponent.TodoItems...
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 16:53:27