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

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;

关键修复点说明

  1. 初始化startDate为周一周一:通过辅助函数getMondayOfWeek确保初始日期是当前周的周一,表头日期对应正确。
  2. 避免修改原Date对象:所有日期计算都创建新的Date实例,不直接修改state中的startDate,防止state被意外篡改。
  3. 修复工时计算的类型问题:将输入的字符串转为数字,避免字符串拼接导致的total计算错误。
  4. 优化表头日期计算:直接使用数组索引代替days.indexOf(day),更高效且逻辑清晰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 01:01:10