React待办列表重复添加单个字符时显示空白问题求助
问题原因分析
你的待办列表出现重复添加单个字符时显示空白的问题,核心原因有两个:
- 状态类型不一致:
task初始状态是字符串,输入时被改为对象,提交后又重置为字符串,这种类型来回切换导致提交时可能将字符串而非对象存入items数组。当items中存在字符串元素时,Todos组件通过...i展开字符串会得到索引键(比如'a'展开为{0: 'a'}),组件期望的task属性会变成undefined,从而显示空白。 - 非受控输入框+状态同步问题:输入框未绑定
value到状态,属于非受控组件,提交时的reset()和setTask("")操作会导致输入框值与状态不同步,在快速操作单个字符时,状态可能未及时更新为对象就被提交。
另外,使用crypto.randomUUID()作为列表项key是错误的——每次渲染都会生成新key,导致React无法复用组件,虽不是空白问题的直接原因,但会引发性能问题。
修复方案
1. 统一状态类型
将task状态改为仅保存输入文本(始终为字符串),在提交时再生成id并创建对象存入items。
2. 改为受控输入框
绑定输入框的value到task状态,确保输入值与状态完全同步。
3. 使用正确的key
用每个待办项的唯一id作为key,让React正确复用组件。
4. 优化状态更新方式
使用函数式更新setItems,确保基于最新状态修改数组。
修改后的代码
App.jsx
import { useState } from "react"; import "./index.css"; import Todos from "./Todos"; export default function App() { return ( <> <header> <h1>TODO</h1> </header> <Tasks /> </> ); } function Tasks() { // task仅保存输入文本,始终为字符串类型 let [task, setTask] = useState(""); let [items, setItems] = useState([]); // 使用待办项的id作为key let p = items.map((i) => { return ( <Todos {...i} key={i.id} handleDelete={handleDelete} /> ); }); function handleDelete(id) { let revised = items.filter((p) => p.id !== id); setItems(revised); } function clearList() { setTask(""); setItems([]); } return ( <div className="test"> <form className="forms" onSubmit={(e) => { e.preventDefault(); // 提交时生成id,创建完整的待办项对象 setItems(prevItems => [...prevItems, { task, id: crypto.randomUUID() }]); setTask(""); }} > <input className="input-area" required type="text" name="text" id="text" placeholder="Create a new todo.." // 受控组件,绑定value到task状态 value={task} onChange={(e) => { setTask(e.target.value); }} /> </form> <div className="to-do-items"> {p} <div className="single-item task-count"> <article className="tata"> {p.length > 1 ? ( <p>{p.length} items left</p> ) : ( <p>{p.length} item left</p> )} </article> <article> <button onClick={clearList}>Clear All</button> </article> </div> </div> </div> ); }
Todos.jsx(无需修改)
import "./index.css"; import { CgClose } from "react-icons/cg"; export default function Todos({ task, id, handleDelete }) { return ( <article className="single-item"> <div className="task-area">{task}</div> <button className="cross" onClick={() => { handleDelete(id); }} > <CgClose size={25} style={{ color: "white" }} /> </button> </article> ); }
内容的提问来源于stack exchange,提问作者ehtachi
相关产品推荐
相关产品推荐

