如何在课程endDate前10分钟自动触发studentUpdateAttendanceStart无需刷新页面
问题根源
- 你在组件顶层声明的
now仅会在组件每次渲染时计算一次,用户不刷新页面的话这个值不会自动更新,自然无法触发时间区间判断逻辑 - 直接把
now加入useEffect依赖会导致每次时间更新触发重渲染,进而无限执行useEffect,不符合预期
最优实现方案
核心思路是用定时器做定时检查,把时间计算放到回调内实时获取,同时做好定时器清理:
useEffect(() => { // 考勤状态已经提交的话直接跳过,不需要定时器 if (student.attendanceStatus.length) return; const allowAttendanceUpdateAfter = moment(endDate).subtract(10, 'minutes').toDate(); const endDateTime = moment(endDate).toDate(); const startDateTime = moment(startDate).toDate(); let timer; const checkAttendance = () => { const currentNow = moment().toDate(); // 已经过了课程结束时间,清理定时器不再检查 if (currentNow > endDateTime) { clearInterval(timer); return; } // 不在可提交时间区间内,跳过本次检查 if (currentNow < allowAttendanceUpdateAfter) return; // 执行考勤判断逻辑 if (allowAttendanceUpdateAfter - startDateTime < currentNow - studentTimestamp) { studentUpdateAttendanceStart(courseDocId, { ...student, id, attendanceStatus: 'Present' }); } else { studentUpdateAttendanceStart(courseDocId, { ...student, id, attendanceStatus: 'Absent' }); } // 提交完成后清理定时器 clearInterval(timer); }; // 首次计算距离可提交时间的差值,优化定时器触发时机 const firstDelay = Math.max(0, allowAttendanceUpdateAfter - Date.now()); // 先设置一次性定时器到可提交时间,之后每分钟检查一次 timer = setTimeout(() => { checkAttendance(); timer = setInterval(checkAttendance, 60 * 1000); }, firstDelay); // 组件卸载/依赖变化时清理定时器 return () => { clearTimeout(timer); clearInterval(timer); }; // 仅添加不会频繁变化的依赖项即可 }, [courseDocId, endDate, id, startDate, student, studentTimestamp, studentUpdateAttendanceStart]);
优化说明
- 首次触发延迟根据当前时间和可提交时间的差值动态计算,不会产生多余的空轮询,性能开销极低
- 考勤提交成功或课程结束后都会立刻清理定时器,不会产生冗余执行
- 所有依赖都是稳定的业务参数,不会出现重复触发的问题
内容的提问来源于stack exchange,提问作者twfm_11
相关产品推荐
相关产品推荐

