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

