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
相关产品推荐
相关产品推荐

