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
相关产品推荐
相关产品推荐

