React日历JavaScript Date异常 周重复首周丢失问题排查修复
问题原因
- 周起始计算逻辑错误:你以周一为每周第一天,但原偏移公式
startDate.getDate() - startDate.getDay() + 1未单独处理周日场景:JS中getDay()返回0代表周日,此时公式会给日期+1直接跳到下周一,导致包含当月1号的首周被直接跳过。 - 循环终止逻辑漏洞:仅通过
getMonth()/getFullYear()大小判断是否超出当月,未考虑月份值0-11循环的特性:跨年场景下上年12月的月份值为11,大于当年1月的月份值0,会误将跨上年的首周判定为超出范围直接截断;同时该判断也无法正确覆盖月末跨月周的边界场景。 - 列表缺失稳定唯一key:所有循环渲染的
SingleWeek、SingleDay组件都未传入key属性,React在虚拟DOM调和阶段会错误复用组件节点,这就是你观察到「push组件前打印日期完全正确、组件内部拿到props不符合预期、第二周重复渲染」的核心原因。
修复方案
- 修正周起始偏移逻辑:单独处理周日的偏移值,确保所有周都从周一开始
- 替换终止判断逻辑:先计算当月最后一天的时间戳,当周起始日期超过当月最后一天时终止循环,彻底解决跨月、跨年的边界判断错误
- 给所有列表项添加基于日期时间戳的稳定唯一key,避免React组件复用错误
- 日期计算时统一基于新Date实例操作,规避JS Date作为引用类型被意外修改的隐性问题
修复后代码
整月日期生成函数
function getWholeMonth(curDate) { const ans = []; const focusDate = new Date(curDate); // 重置为当月1号 focusDate.setDate(1); // 计算当月最后一天:下个月第0天即为当月最后一天 const lastDayOfMonth = new Date(focusDate.getFullYear(), focusDate.getMonth() + 1, 0); // 计算首周的周一日期 const startDate = new Date(focusDate); const weekDay = startDate.getDay(); // 周日特殊处理:减6天回到上周一,其余日期减(weekDay-1)回到当周周一 const mondayOffset = weekDay === 0 ? 6 : weekDay - 1; startDate.setDate(startDate.getDate() - mondayOffset); // 循环生成每一周的组件 while (startDate.getTime() <= lastDayOfMonth.getTime()) { const weekStart = new Date(startDate); const weekEnd = new Date(startDate); weekEnd.setDate(weekEnd.getDate() + 6); // 周一加6天即为当周周日 // 用周起始时间戳作为唯一key,避免组件复用错误 ans.push(<SingleWeek key={weekStart.getTime()} startDate={weekStart} endDate={weekEnd} />); // 偏移到下一周 startDate.setDate(startDate.getDate() + 7); } return ans; }
周组件修复
function SingleWeek(props) { const { startDate } = props; const week = []; // 用let声明块级作用域变量,避免var的变量提升问题 for (let i = 0; i < 7; i++) { const curDate = new Date(startDate); curDate.setDate(curDate.getDate() + i); // 单日组件用当天时间戳作为唯一key week.push(<SingleDay key={curDate.getTime()} currentDate={curDate} />); } return ( <div style={{display: "flex"}}> {week} </div> ); }
注:原
SingleWeek组件中并未使用endDate参数,可根据实际业务需求决定是否保留。
内容的提问来源于stack exchange,提问作者Hoangdz
相关产品推荐
相关产品推荐

