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

React带日期字段的习惯追踪表格开发求助

习惯追踪表格开发指导

我要做一个习惯追踪表格,需求如下:

  • 用户选择年月后,表格第一列显示该月份的所有日期(比如1-30)
  • 其余列用来添加习惯,用户能在对应日期的习惯列里填X标记完成(示例:29, X, X, , X)
  • 默认包含日期、Habit1、Habit2、Habit3、Habit4这几列

现在我写了部分React代码,但后续不知道怎么推进,求技术指导:

import React, { useState } from 'react';
import './HabitTable.css';

function HabitTable() {

  const today = new Date();
  const todayDate = today.getDate();
  const daysArray = [];

  const [month, setMonth] = useState('');
  const [year, setYear] = useState('');
  const [dateArray, setDateArray] = useState([]);
  const [dailyHabits, setDailyHabits] = useState([]); 

  const [addHabit, setAddHabit] = useState('');

  function addHabits() {
        if (addHabit.trim() !== '') {
      const updatedHabits = [...dailyHabits];
      updatedHabits.push(addHabit);
      setDailyHabits(updatedHabits);
      setAddHabit(''); 
   }
    
  }

  function getCorrectDates(month, year) {
    const numDays = new Date(year, month, 0).getDate();

    for (let i = 0; i < numDays; i++) {
      daysArray[i] = i + 1;
    }

    return daysArray;
  }

  function handleDates() {
    setDateArray(getCorrectDates(month, year));
  }

  return (
    <div>
      <div className='select-dates'>
        <input placeholder='month' onChange={(e) => setMonth(e.target.value)} />
        <input placeholder='year' onChange={(e) => setYear(e.target.value)} />
        <button onClick={handleDates}>Submit</button>
      </div>

      <div>
        <h1>Days</h1>
        <div>
          <input placeholder='add habit' value={addHabit} onChange={(e) => setAddHabit(e.target.value)} />
          <button onClick={addHabits}>Add</button>
        </div>
        <div className='days'>
          {dateArray.map((day) => (
            <div key={day}>
              <input disabled placeholder={day} />
            </div>
          ))}
          {Object.keys(dailyHabits).map((habitKey) => (
            
            <input key={todayDate} placeholder={dailyHabits[habitKey] + todayDate} />
          ))}
        </div>
      </div>
    </div>
  );
}

核心问题修正与功能完善

1. 状态结构重构

原代码只存习惯名称,没法记录每日完成状态,需要调整状态:

  • habits:存储所有习惯名称(包含默认的Habit1-4)
  • habitRecords:二维数组,habitRecords[日期索引][习惯索引] 存储对应状态(X或空)

2. 年月选择逻辑修正

用户输入的月份是1-12,但Date构造函数需要0-11的数字,必须做转换;同时限制输入为合法数字,避免无效值。

3. 表格布局优化

用标准<table>标签实现语义化表格,行对应日期、列对应日期+习惯,布局更清晰易维护。

4. 编辑状态绑定

给每个习惯单元格的输入框绑定状态,输入X或空值时实时更新记录。


修改后的完整代码

import React, { useState, useEffect } from 'react';
import './HabitTable.css';

function HabitTable() {
  const today = new Date();
  // 初始化默认习惯列表
  const defaultHabits = ['Habit1', 'Habit2', 'Habit3', 'Habit4'];
  
  const [month, setMonth] = useState(today.getMonth() + 1); // 默认当前月份(1-12)
  const [year, setYear] = useState(today.getFullYear()); // 默认当前年份
  const [dateArray, setDateArray] = useState([]);
  const [habits, setHabits] = useState(defaultHabits);
  const [addHabit, setAddHabit] = useState('');
  // 二维数组存储每日习惯完成状态:[ [第1天的所有习惯状态], [第2天的所有习惯状态], ... ]
  const [habitRecords, setHabitRecords] = useState([]);

  // 根据年月生成日期数组,同时初始化习惯记录
  const getCorrectDates = (monthNum, yearNum) => {
    const adjustedMonth = monthNum - 1; // 转成Date需要的0-11格式
    const numDays = new Date(yearNum, adjustedMonth + 1, 0).getDate();
    const days = Array.from({ length: numDays }, (_, i) => i + 1);
    
    // 初始化每个日期的习惯状态为空白
    const initialRecords = Array.from({ length: numDays }, () => 
      Array(habits.length).fill('')
    );
    setHabitRecords(initialRecords);
    
    return days;
  };

  // 提交年月时更新日期数组
  const handleDates = () => {
    if (!month || !year) return;
    const parsedMonth = parseInt(month);
    const parsedYear = parseInt(year);
    // 校验年月合法性
    if (parsedMonth < 1 || parsedMonth > 12 || isNaN(parsedYear)) return;
    
    setDateArray(getCorrectDates(parsedMonth, parsedYear));
  };

  // 添加新习惯
  const addNewHabit = () => {
    const newHabit = addHabit.trim();
    if (!newHabit || habits.includes(newHabit)) return;
    
    const updatedHabits = [...habits, newHabit];
    setHabits(updatedHabits);
    
    // 给所有日期的新习惯列添加空白状态
    const updatedRecords = habitRecords.map(record => [...record, '']);
    setHabitRecords(updatedRecords);
    
    setAddHabit('');
  };

  // 更新某一天的某个习惯状态
  const updateHabitRecord = (dayIndex, habitIndex, value) => {
    const updatedRecords = [...habitRecords];
    updatedRecords[dayIndex][habitIndex] = value.trim() === 'X' ? 'X' : '';
    setHabitRecords(updatedRecords);
  };

  // 组件加载时自动初始化当前年月的日期
  useEffect(() => {
    handleDates();
  }, []);

  return (
    <div className="habit-tracker">
      {/* 年月选择区 */}
      <div className='select-dates'>
        <input 
          type="number" 
          min="1" 
          max="12" 
          value={month} 
          onChange={(e) => setMonth(e.target.value)} 
          placeholder="月份"
        />
        <input 
          type="number" 
          min="2000" 
          max="2100" 
          value={year} 
          onChange={(e) => setYear(e.target.value)} 
          placeholder="年份"
        />
        <button onClick={handleDates}>确认</button>
      </div>

      {/* 习惯管理区 */}
      <div className="habit-management">
        <input 
          placeholder='添加新习惯' 
          value={addHabit} 
          onChange={(e) => setAddHabit(e.target.value)} 
        />
        <button onClick={addNewHabit}>添加</button>
      </div>

      {/* 习惯表格 */}
      {dateArray.length > 0 && (
        <table className="habit-table">
          <thead>
            <tr>
              <th>日期</th>
              {habits.map((habit, index) => (
                <th key={index}>{habit}</th>
              ))}
            </tr>
          </thead>
          <tbody>
            {dateArray.map((day, dayIndex) => (
              <tr key={day}>
                <td>{day}</td>
                {habits.map((_, habitIndex) => (
                  <td key={habitIndex}>
                    <input
                      value={habitRecords[dayIndex][habitIndex]}
                      onChange={(e) => updateHabitRecord(dayIndex, habitIndex, e.target.value)}
                      maxLength="1"
                      placeholder="X"
                    />
                  </td>
                ))}
              </tr>
            ))}
          </tbody>
        </table>
      )}
    </div>
  );
}

export default HabitTable;

额外说明

  • 用<table>实现标准表格布局,语义化更强,样式调试更简单
  • 初始化时自动加载当前年月数据,提升用户体验
  • 限制年月输入为数字类型,避免非法值导致的错误
  • 添加新习惯时自动扩展所有日期的记录列,无需手动同步状态

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 21:30:56