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

React Todo List添加数据后不显示,点击页面才出现的修复咨询

React Todo List添加待办后不即时显示的修复方案

问题描述

我正在开发一个具备分页功能(每5条数据一页)的React Todo List,程序运行无报错,但添加待办项后不会立即显示,只有点击页面后才会出现。尝试修改handleSubmit函数仍未解决,请问该如何修复?

原代码

import React, { useState, useEffect } from "react";
import "./App.css";

export default function App() {
  const [todoData, setTodoData] = useState([]);
  const [value, setValue] = useState("");

  const [currentPage, setCurrentPage] = useState(1);
  const [currentTodos, setCurrentTodos] = useState([]);
  const todosPerPage = 5;

  const btnStyle = {
    color: "#fff",
    border: "none",
    padding: "5px 9px",
    borderRadius: "50%",
    cursor: "pointer",
    float: "right",
  };

  const getStyle = (finished) => {
    return {
      padding: "10px",
      borderBottom: "1px #ccc dotted",
      textDecoration: finished ? "line-through" : "none",
    };
  };

  const handleClick = (id) => {
    let newTodoData = todoData.filter((data) => data.id !== id);
    setTodoData(newTodoData);
  };

  const handleChange = (e) => {
    setValue(e.target.value);
  };

  const handleSubmit = (e) => {
    e.preventDefault();

    let newTodo = {
      id: Date.now(),
      title: value,
      finished: false,
    };

    setTodoData((prev) => [...prev, newTodo]);

    if ((todoData.length + 1) % todosPerPage === 1) {
      setCurrentPage((prevPage) => prevPage + 1);
    }

    setValue("");
  };

  const handleFinished = (id) => {
    let newTodoData = todoData.map((data) => {
      if (data.id === id) {
        data.finished = !data.finished;
      }
      return data;
    });
    setTodoData(newTodoData);
  };

  useEffect(() => {
    const indexOfLastTodo = currentPage * todosPerPage;
    const indexOfFirstTodo = indexOfLastTodo - todosPerPage;
    const currentTodos = todoData
      .filter(
        (_, index) => index >= indexOfFirstTodo && index < indexOfLastTodo
      )
      .slice(0, todosPerPage);

    setCurrentTodos(currentTodos);
  }, [todoData, currentPage, todosPerPage]);

  return (
    <div className="container">
      <div className="todoBlock">
        <div className="title">
          <h1 className="title" style={{ textAlign: "center" }}>
            Todo List
          </h1>
        </div>
        {currentTodos.map((data) => (
          <div style={getStyle(data.finished)} key={data.id}>
            <input
              type="checkbox"
              onChange={() => handleFinished(data.id)}
              defaultChecked={false}
            />
            {data.title}
            <button style={btnStyle} onClick={() => handleClick(data.id)}>
              x
            </button>
          </div>
        ))}

        <form style={{ display: "flex" }} onSubmit={handleSubmit}>
          <input
            type="text"
            name="value"
            style={{ flex: "10", padding: "5px" }}
            placeholder="Here"
            value={value}
            onChange={handleChange}
          />

          <button className="btn" type="submit" style={{ flext: "1" }}>
            Enter
          </button>
        </form>

        <div className="pagination">
          {Array.from(
            { length: Math.ceil(todoData.length / 5) },
            (_, i) => i + 1
          ).map((pageNumber) => (
            <button
              key={pageNumber}
              onClick={() => setCurrentPage(pageNumber)}
              className={currentPage === pageNumber ? "active" : ""}
            >
              {pageNumber}
            </button>
          ))}
        </div>
      </div>
    </div>
  );
}

修复方案

1. 修正handleSubmit中的翻页判断逻辑

setTodoData是异步操作,直接使用todoData.length获取的是更新前的旧值,导致翻页判断错误。需要在setTodoData的函数式更新中,基于最新的数组长度判断是否翻页:

修改后的handleSubmit:

const handleSubmit = (e) => {
  e.preventDefault();

  if (!value.trim()) return; // 空输入不处理

  let newTodo = {
    id: Date.now(),
    title: value,
    finished: false,
  };

  setTodoData((prev) => {
    const newTodos = [...prev, newTodo];
    // 新数组长度刚好是每页数量的倍数+1时,切换到新页面
    if (newTodos.length % todosPerPage === 1) {
      setCurrentPage(prevPage => prevPage + 1);
    }
    return newTodos;
  });

  setValue("");
};

2. 修复handleFinished的不可变更新问题

直接修改原数组元素违反React不可变状态原则,可能导致组件无法检测到状态变化。需要返回新对象:

修改后的handleFinished:

const handleFinished = (id) => {
  setTodoData(prev => prev.map(data => 
    data.id === id ? {...data, finished: !data.finished} : data
  ));
};

3. 简化分页计算逻辑(可选)

原useEffect中的filter+slice可以简化为直接使用slice,逻辑更清晰:

修改后的useEffect:

useEffect(() => {
  const startIndex = (currentPage - 1) * todosPerPage;
  const endIndex = startIndex + todosPerPage;
  setCurrentTodos(todoData.slice(startIndex, endIndex));
}, [todoData, currentPage, todosPerPage]);

修复后的完整代码

import React, { useState, useEffect } from "react";
import "./App.css";

export default function App() {
  const [todoData, setTodoData] = useState([]);
  const [value, setValue] = useState("");

  const [currentPage, setCurrentPage] = useState(1);
  const [currentTodos, setCurrentTodos] = useState([]);
  const todosPerPage = 5;

  const btnStyle = {
    color: "#fff",
    border: "none",
    padding: "5px 9px",
    borderRadius: "50%",
    cursor: "pointer",
    float: "right",
  };

  const getStyle = (finished) => {
    return {
      padding: "10px",
      borderBottom: "1px #ccc dotted",
      textDecoration: finished ? "line-through" : "none",
    };
  };

  const handleClick = (id) => {
    setTodoData(prev => prev.filter(data => data.id !== id));
  };

  const handleChange = (e) => {
    setValue(e.target.value);
  };

  const handleSubmit = (e) => {
    e.preventDefault();

    if (!value.trim()) return;

    let newTodo = {
      id: Date.now(),
      title: value,
      finished: false,
    };

    setTodoData((prev) => {
      const newTodos = [...prev, newTodo];
      if (newTodos.length % todosPerPage === 1) {
        setCurrentPage(prevPage => prevPage + 1);
      }
      return newTodos;
    });

    setValue("");
  };

  const handleFinished = (id) => {
    setTodoData(prev => prev.map(data => 
      data.id === id ? {...data, finished: !data.finished} : data
    ));
  };

  useEffect(() => {
    const startIndex = (currentPage - 1) * todosPerPage;
    const endIndex = startIndex + todosPerPage;
    setCurrentTodos(todoData.slice(startIndex, endIndex));
  }, [todoData, currentPage, todosPerPage]);

  return (
    <div className="container">
      <div className="todoBlock">
        <div className="title">
          <h1 className="title" style={{ textAlign: "center" }}>
            Todo List
          </h1>
        </div>
        {currentTodos.map((data) => (
          <div style={getStyle(data.finished)} key={data.id}>
            <input
              type="checkbox"
              onChange={() => handleFinished(data.id)}
              checked={data.finished} // 改为受控组件,使用checked而非defaultChecked
            />
            {data.title}
            <button style={btnStyle} onClick={() => handleClick(data.id)}>
              x
            </button>
          </div>
        ))}

        <form style={{ display: "flex" }} onSubmit={handleSubmit}>
          <input
            type="text"
            name="value"
            style={{ flex: "10", padding: "5px" }}
            placeholder="输入待办事项"
            value={value}
            onChange={handleChange}
          />

          <button className="btn" type="submit" style={{ flex: "1" }}> {/* 修正拼写错误flext→flex */}
            添加
          </button>
        </form>

        <div className="pagination">
          {Array.from(
            { length: Math.ceil(todoData.length / todosPerPage) }, // 替换硬编码的5为变量
            (_, i) => i + 1
          ).map((pageNumber) => (
            <button
              key={pageNumber}
              onClick={() => setCurrentPage(pageNumber)}
              className={currentPage === pageNumber ? "active" : ""}
            >
              {pageNumber}
            </button>
          ))}
        </div>
      </div>
    </div>
  );
}

额外优化:将checkbox改为受控组件(使用checked={data.finished}),修正按钮样式中的拼写错误flext→flex,分页按钮数量计算使用变量todosPerPage而非硬编码5,添加空输入判断。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 23:07:51