React动态列日期工时表:如何匹配数据到对应日期列
我正在开发一款用于展示用户当月每日对应工时的表格组件,由于不同月份天数存在差异,表格列需要动态生成。目前已经完成基础结构搭建:表头可根据选中的年、月展示对应数量的日期列,也可以正常通过API拉取数据,但始终无法将数据中的日期与表格的日期列做正确匹配。
我制作了简化版示例展示当前开发进度,示例中保留了核心逻辑。
实际业务场景下,我使用的数据包含两个日期对象:开始时间与结束时间。通常工时记录不会跨天,但也存在跨天的可能性,当前表格开发阶段我先仅提取开始时间对应的日期做匹配,这也是示例代码中仅保留start字段的原因。
我曾尝试通过如下逻辑提取工时记录对应的当月日期:
setDataDates( const dataDay = new Date(data.start).getDate() )
但提取到日期后,我不清楚如何将对应工时渲染到表格中正确的日期列下,寻求开发指导。
问题更新
我一度以为已经接近实现,至少页面可以正常渲染,但示例抛出了错误:Objects are not valid as a React child (found: object with keys {day}),报错位置在表头遍历渲染日期的部分。这部分逻辑之前是可以正常运行的,我一开始没定位到出错原因,后来发现是我将数组元素改成了{key: value}结构,map遍历时没有取day.day属性,该问题已经修复,代码已同步更新。
但目前核心的日期数据匹配功能仍然无法正常运行,当前代码如下:
import "./styles.css"; import React from "react"; export default function App() { const data = [ { start: "2022-04-02", hours: 2 }, { start: "2022-04-06", hours: 3 }, { start: "2022-04-11", hours: 1 } ]; const [newData, setNewData] = React.useState(); const setDataforTable = () => { const oldData = data.map((data) => ({ day: new Date(data.start).getDate(), hours: data.hours })); console.log("newData", oldData); setNewData(oldData); }; const [days, setDays] = React.useState([]); const settingDays = () => { const daysInMonth = 30; let dayInfo = []; for (let i = 1; i <= daysInMonth; i++) { dayInfo.push(i); let result = dayInfo.map((i) => ( { day: i } )); console.log("day ", result); setDays(result); } }; const totalHours = data.map((i) => i.hours).reduce((a, b) => a + b, 0); let tableDisplay; if (days !== []) { tableDisplay = ( <div> <table> <thead> <tr> <th> Date </th> {days && days.map((day) => <th key={day}>{day.day}</th>)} <th>Total Hours</th> </tr> </thead> <tbody> <tr> <td>Hours</td> {newData && newData.map((d) => <td key={d.day}>{d.day === days.day ? d.hours : 0}</td> )} <td>{totalHours}</td> </tr> </tbody> </table> </div> ); } return ( <div> <button onClick={() => { setDataforTable(), settingDays(); }} > Click </button> {tableDisplay} </div> ); }
核心问题是单元格遍历逻辑写反了:表格列数和当月天数一致,所以渲染表体单元格时要遍历生成好的日期数组,而不是遍历工时数据数组——工时数据的长度和当月天数绝大多数情况不相等,自然没法对齐到所有列。
除此之外代码里还有几个逻辑问题需要同步修正:
- 数组判断逻辑错误:
days !== []永远为真,JS中数组是引用类型,两个空数组比较不会相等,直接判断days.length即可 settingDays函数里不需要在for循环内部反复执行map和setDays,循环生成完所有日期后一次赋值即可- 日期匹配时,
days是数组,不存在days.day属性,需要针对每个遍历到的日期列,去工时数据里找对应日期的工时值,找不到就显示0 - 计算总工时的时候不要直接用原始
data计算,要兼容后续动态拉取的接口数据
可以按照以下思路调整逻辑:
- 生成日期列:直接生成1到当月天数的数组即可,不需要额外包
{day: i}的结构,减少取值错误 - 预处理工时数据:提前把工时数据转成
{日期: 工时}的对象结构,匹配的时候不需要每次遍历数组找值,性能更好 - 渲染单元格:遍历日期数组,对每个日期,从处理好的工时映射里取对应值,没有就显示0
参考修正代码:
import "./styles.css"; import React from "react"; export default function App() { // 原始接口数据 const data = [ { start: "2022-04-02", hours: 2 }, { start: "2022-04-06", hours: 3 }, { start: "2022-04-11", hours: 1 } ]; const [hoursMap, setHoursMap] = React.useState({}); const [days, setDays] = React.useState([]); const [totalHours, setTotalHours] = React.useState(0); const initTableData = () => { // 1. 生成当月日期列,这里示例固定30天,实际业务中可以根据选中年月动态计算当月天数 const daysInMonth = 30; const dayList = Array.from({length: daysInMonth}, (_, i) => i+1); setDays(dayList); // 2. 处理工时数据,转成 日期:工时 的映射结构,方便匹配 const map = {}; let total = 0; data.forEach(item => { const day = new Date(item.start).getDate(); map[day] = item.hours; total += item.hours; }) setHoursMap(map); setTotalHours(total); }; return ( <div> <button onClick={initTableData}> 加载表格 </button> {days.length > 0 && ( <table> <thead> <tr> <th>分类</th> {days.map(day => <th key={day}>{day}日</th>)} <th>总工时</th> </tr> </thead> <tbody> <tr> <td>工时</td> {/* 遍历日期列,逐列匹配工时 */} {days.map(day => <td key={day}>{hoursMap[day] || 0}</td>)} <td>{totalHours}</td> </tr> </tbody> </table> )} </div> ); }
如果后续要支持跨天工时,只需要在处理hoursMap的时候,根据开始、结束时间计算覆盖的所有日期,按规则拆分工时到对应日期即可,当前的表格渲染逻辑不需要改动。
内容的提问来源于stack exchange,提问作者Jowz

