React.js Todo列表:实现待办项在待办/已完成列表间双向迁移的问题求助
修复React待办事项双向迁移的问题
嘿,我仔细看了你的代码和遇到的问题,根源出在handleToggle函数的逻辑设计上——你现在只处理了从未完成列表到已完成列表的单向添加,却没把原列表里的项移除,而且完全没考虑反向迁移的情况,再加上直接修改state对象的反模式,才导致了这两个bug。咱们一步步来修复:
问题根源拆解
- 项重复出现在两个列表:你点击toggle时,只是把修改了
completed的项push到donelist,但没从list里删掉它,同时还直接修改了state里的原对象(违反React不可变性原则),导致list里依然保留着已标记为完成的项。 - 已完成列表生成重复项:原
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
相关产品推荐
相关产品推荐

