React useState工时表组件日期显示异常问题求助
问题分析与修复方案
你的两个问题本质都是直接修改了React state中的Date引用类型对象,以及日期计算逻辑错误导致的:
问题1:周切换时日期随机变化
你在显示周范围的代码中使用了startDate.setDate(startDate.getDate() + 6),这个方法会直接修改state中的原startDate对象(Date是引用类型),导致组件重渲染时state被意外篡改,日期出现随机变化。
问题2:输入内容时表头日期改变
每次输入触发state更新(data变化),组件会重渲染。重渲染时,表头的日期计算依赖被修改过的startDate,同时原来的日期计算逻辑(用days.indexOf(day))结合被篡改的startDate,导致日期显示混乱。
另外,你的初始startDate是当前日期,不一定是周一开始,这也会导致表头日期对应错误(比如当天是周三,表头的Mon会显示成周三而不是周一)。
完整修复代码
import { useState } from 'react'; const TimeSheet = () => { const days = ["Mon", "Tue", "Wed", "Thurs", "Fri", "Sat", "Sun"]; // 辅助函数:获取传入日期所在周的周一 const getMondayOfWeek = (date) => { const d = new Date(date); const day = d.getDay(); // 周日的话需要往前推6天,其他日期推(day-1)天到周一 const diff = d.getDate() - day + (day === 0 ? -6 : 1); return new Date(d.setDate(diff)); }; // 初始化startDate为当前周的周一 const [startDate, setStartDate] = useState(getMondayOfWeek(new Date())); const [data, setData] = useState([ { project: 'Project A', hours: [0, 0, 0, 0, 0, 0, 0], total: 0 }, { project: 'Project B', hours: [0, 0, 0, 0, 0, 0, 0], total: 0 }, ]); const handlePreviousWeek = () => { const prevWeek = new Date(startDate); prevWeek.setDate(startDate.getDate() - 7); setStartDate(prevWeek); }; const handleNextWeek = () => { const nextWeek = new Date(startDate); nextWeek.setDate(startDate.getDate() + 7); setStartDate(nextWeek); }; const handleAddRow = () => { setData([ ...data, { project: `Project ${data.length + 1}`, hours: [0, 0, 0, 0, 0, 0, 0], total: 0 }, ]); }; const handleDeleteRow = (index) => { const updatedData = [...data]; updatedData.splice(index, 1); setData(updatedData); }; const handleSaveHours = () => { console.log('Hours saved:', data); }; const handleHoursChange = (projectIndex, dayIndex, value) => { const updatedData = [...data]; // 将输入值转为数字,空值或非数字转为0 const numValue = Number(value) || 0; updatedData[projectIndex].hours[dayIndex] = numValue; // 重新计算总工时,确保每个值都是数字 updatedData[projectIndex].total = updatedData[projectIndex].hours.reduce( (acc, cur) => acc + (Number(cur) || 0), 0 ); setData(updatedData); }; // 计算周结束日期(不修改原startDate) const endDate = new Date(startDate); endDate.setDate(startDate.getDate() + 6); return ( <div> <h1>Project Simple Time Sheet</h1> <div> <button onClick={handlePreviousWeek}>Previous Week</button> <span>{`${startDate.toLocaleDateString('de-DE')} - ${endDate.toLocaleDateString('de-DE')}`}</span> <button onClick={handleNextWeek}>Next Week</button> </div> <table> <thead> <tr> <th>Project</th> {/* 直接用数组索引计算日期,避免indexOf的性能问题和逻辑错误 */} {days.map((day, index) => ( <th key={day}> {day} <br /> {new Date( startDate.getFullYear(), startDate.getMonth(), startDate.getDate() + index ).toLocaleDateString('de-DE')} </th> ))} <th>Total</th> <th></th> </tr> </thead> <tbody> {data.map((item, index) => ( <tr key={index}> <td>{item.project}</td> {item.hours.map((hour, dayIndex) => ( <td key={dayIndex}> <input type="number" value={hour} onChange={(e) => handleHoursChange(index, dayIndex, e.target.value)} /> </td> ))} <td>{item.total}</td> <td><button onClick={() => handleDeleteRow(index)}>Delete</button></td> </tr> ))} <tr> <td></td> {Array.from({ length: 7 }).map((_, index) => <td key={index}></td>)} <td></td> <td></td> </tr> </tbody> </table> <button onClick={handleAddRow}>Add Row</button> <button onClick={handleSaveHours}>Save Hours</button> <button onClick={() => console.log('Hours submitted:', data)}>Submit</button> </div> ); }; export default TimeSheet;
关键修复点说明
- 初始化startDate为周一周一:通过辅助函数
getMondayOfWeek确保初始日期是当前周的周一,表头日期对应正确。 - 避免修改原Date对象:所有日期计算都创建新的Date实例,不直接修改state中的startDate,防止state被意外篡改。
- 修复工时计算的类型问题:将输入的字符串转为数字,避免字符串拼接导致的total计算错误。
- 优化表头日期计算:直接使用数组索引代替
days.indexOf(day),更高效且逻辑清晰。
内容的提问来源于stack exchange,提问作者Codeworld
相关产品推荐
相关产品推荐

