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

React中使用State展示筛选列表时遇列表覆盖问题

解决React TodoList切换任务时列表被覆盖的问题

核心问题在于你维护了两个独立的状态(tasks和completedTasks),这会导致数据同步困难,切换任务时容易出现覆盖原有列表的情况。正确的思路是只维护一个完整的任务列表状态,通过筛选计算得到活跃任务和已完成任务,而非分开存储两个状态。

步骤1:简化状态定义

只保留一个存储所有任务的状态,移除单独的completedTasks状态,用useMemo实时计算分类后的任务列表:

import { useState, useEffect, useMemo } from 'react';

const LOCAL_STORAGE_KEY = 'todo_tasks';

function TodoApp() {
  // 仅维护一个完整的任务列表状态
  const [tasks, setTasks] = useState([]);

  // 实时筛选活跃任务和已完成任务,useMemo避免重复计算
  const activeTasks = useMemo(() => {
    return tasks.filter(task => task.active === true);
  }, [tasks]);

  const completedTasks = useMemo(() => {
    return tasks.filter(task => task.active === false);
  }, [tasks]);

  // 从LocalStorage读取完整任务数据
  useEffect(() => {
    const storedTasks = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY));
    if (storedTasks) {
      setTasks(storedTasks);
    }
  }, []);

  // 任务变化时自动同步到LocalStorage
  useEffect(() => {
    localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(tasks));
  }, [tasks]);

步骤2:修复新增任务逻辑

原来的addTask直接替换了整个任务数组,改为追加新任务到现有列表:

// 新增任务:确保新任务active属性为true
  const addTask = (newTask) => {
    const taskWithActive = { ...newTask, active: true, id: Date.now() }; // 加id确保唯一性
    setTasks(prevTasks => [...prevTasks, taskWithActive]);
  };

步骤3:调整任务切换逻辑

把切换逻辑放到父组件,直接更新完整任务列表,无需手动拆分数据:

// 切换任务的active状态
  const toggleTaskActive = (taskId) => {
    setTasks(prevTasks => 
      prevTasks.map(task => {
        if (task.id === taskId) {
          return { ...task, active: !task.active };
        }
        return task;
      })
    );
  };

步骤4:修改List组件调用方式

给两个列表分别传入对应分类的任务和切换函数即可:

return (
    <div>
      <h2>当前任务</h2>
      <List tasks={activeTasks} toggleTask={toggleTaskActive} />
      
      <h2>已完成任务</h2>
      <List tasks={completedTasks} toggleTask={toggleTaskActive} />
    </div>
  );
}

步骤5:简化List组件

List组件只负责渲染任务和触发切换操作:

function List({ tasks, toggleTask }) {
  return (
    <ul>
      {tasks.map(task => (
        <li key={task.id}>
          <input
            type="checkbox"
            checked={!task.active} // 已完成任务对应复选框勾选状态
            onChange={() => toggleTask(task.id)}
          />
          <span>{task.text}</span>
        </li>
      ))}
    </ul>
  );
}

为什么这样能解决问题?

  1. 单一数据源:所有任务都存在tasks状态中,避免了两个状态不同步的问题。
  2. 实时计算分类:activeTasks和completedTasks通过useMemo从完整列表实时筛选,数据始终最新。
  3. 自动持久化:通过useEffect监听tasks变化,自动同步到LocalStorage,无需在每个操作里手动调用存储方法。

这样修改后,切换任务时只会更新对应任务的active属性,两个列表会自动重新渲染,不会出现原有任务被覆盖的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 01:40:13