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

React待办列表重复添加单个字符时显示空白问题求助

问题原因分析

你的待办列表出现重复添加单个字符时显示空白的问题,核心原因有两个:

  1. 状态类型不一致:task初始状态是字符串,输入时被改为对象,提交后又重置为字符串,这种类型来回切换导致提交时可能将字符串而非对象存入items数组。当items中存在字符串元素时,Todos组件通过...i展开字符串会得到索引键(比如'a'展开为{0: 'a'}),组件期望的task属性会变成undefined,从而显示空白。
  2. 非受控输入框+状态同步问题:输入框未绑定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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 18:47:14