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

React.js Todo列表:实现待办项在待办/已完成列表间双向迁移的问题求助

修复React待办事项双向迁移的问题

嘿,我仔细看了你的代码和遇到的问题,根源出在handleToggle函数的逻辑设计上——你现在只处理了从未完成列表到已完成列表的单向添加,却没把原列表里的项移除,而且完全没考虑反向迁移的情况,再加上直接修改state对象的反模式,才导致了这两个bug。咱们一步步来修复:

问题根源拆解

  1. 项重复出现在两个列表:你点击toggle时,只是把修改了completed的项push到donelist,但没从list里删掉它,同时还直接修改了state里的原对象(违反React不可变性原则),导致list里依然保留着已标记为完成的项。
  2. 已完成列表生成重复项:原handleToggle只遍历处理list里的元素,当点击donelist里的项时,函数根本没触发迁移逻辑,再加上原list里还留着对应项,重复操作就会导致donelist被多次添加同一个元素。

完整修复方案

1. 重构handleToggle函数

我们需要处理双向迁移:判断当前项在哪个列表,然后从原列表移除,同时添加修改状态后的新项到目标列表,全程遵循React的不可变性原则:

handleToggle = (item) => {
  // 创建修改了completed状态的新对象(不直接修改原state)
  const updatedItem = { ...item, completed: !item.completed };
  
  // 判断当前项属于哪个列表
  const isInPendingList = this.state.list.some(el => el.id === item.id);

  if (isInPendingList) {
    // 从未完成列表迁移到已完成列表
    this.setState(prevState => ({
      list: prevState.list.filter(el => el.id !== item.id),
      donelist: [...prevState.donelist, updatedItem]
    }));
  } else {
    // 从已完成列表迁移回未完成列表
    this.setState(prevState => ({
      donelist: prevState.donelist.filter(el => el.id !== item.id),
      list: [...prevState.list, updatedItem]
    }));
  }
};

2. 修复handleDelete函数

现在项可能存在于两个列表中,删除时需要同时检查并过滤两个列表:

handleDelete = (item) => {
  this.setState(prevState => ({
    list: prevState.list.filter(el => el.id !== item.id),
    donelist: prevState.donelist.filter(el => el.id !== item.id)
  }));
};

3. 补充列表渲染的key属性

给Item组件加上key属性是React列表渲染的最佳实践,能避免不必要的重渲染和控制台警告。

修复后的完整组件代码

import React from "react";
import { Container, DoneContainer, TaskContainer, Input, Button, FormContainer, OrderList, Div, PendingItem, CompltedItem, Para } from "./App.styles.js";
import "./styles.css";

const Item = ({ item, handleToggle, handleDelete }) => (
  <OrderList item={item}>
    <Div item={item}>{item.value}</Div>
    <button onClick={() => handleToggle(item)}>Toggle</button>
    <button onClick={() => handleDelete(item)}>Delete</button>
  </OrderList>
);

class App extends React.Component {
  state = {
    list: [],
    donelist: [],
    inputValue: "",
    dublicate: false
  };

  handleClick = (e) => {
    this.setState({ inputValue: e.target.value });
  };

  handleDelete = (item) => {
    this.setState(prevState => ({
      list: prevState.list.filter(el => el.id !== item.id),
      donelist: prevState.donelist.filter(el => el.id !== item.id)
    }));
  };

  handleSubmit = (e) => {
    e.preventDefault();
    this.handleList(this.state.inputValue);
    this.setState({ inputValue: "" });
  };

  handleToggle = (item) => {
    const updatedItem = { ...item, completed: !item.completed };
    const isInPendingList = this.state.list.some(el => el.id === item.id);

    if (isInPendingList) {
      this.setState(prevState => ({
        list: prevState.list.filter(el => el.id !== item.id),
        donelist: [...prevState.donelist, updatedItem]
      }));
    } else {
      this.setState(prevState => ({
        donelist: prevState.donelist.filter(el => el.id !== item.id),
        list: [...prevState.list, updatedItem]
      }));
    }
  };

  getDublicate = (value) => {
    return this.state.list.find((item) => item.value === value);
  };

  handleList = (value) => {
    if (this.getDublicate(value)) {
      this.setState({ dublicate: !this.state.dublicate });
    } else {
      const item = { value, id: `${Math.random()}`, completed: false };
      const newList = [...this.state.list, item];
      this.setState({ list: newList });
    }
  };

  render() {
    console.log(this.state.donelist);
    return (
      <Container>
        <FormContainer onSubmit={this.handleSubmit}>
          <Input onChange={this.handleClick} value={this.state.inputValue} />
          <Button>Click</Button>
        </FormContainer>
        <PendingItem>Pending item list: </PendingItem>
        <TaskContainer>
          <ul>
            {this.state.list.map((element) => (
              <Item key={element.id} item={element} handleToggle={this.handleToggle} handleDelete={this.handleDelete} />
            ))}
          </ul>
        </TaskContainer>
        <CompltedItem>Completed item list: </CompltedItem>
        <DoneContainer>
          <ul>
            {this.state.donelist.map((element) => (
              <Item key={element.id} item={element} handleToggle={this.handleToggle} handleDelete={this.handleDelete} />
            ))}
          </ul>
        </DoneContainer>
      </Container>
    );
  }
}

export default App;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 12:52:47