React动态选择数组元素渲染当日及前后日程表问题求助
问题分析与解决方案
核心问题梳理
- 日期比较逻辑错误:直接用
===或</>比较moment对象,且未忽略时分秒导致日期匹配失败 - 异步状态更新时序问题:调用
setCurrentIndex后立即执行initPreviousAndNext,此时currentIndex尚未完成更新 - 状态冗余与取值错误:依赖未更新的
previousIndex/nextIndex获取日程,导致前/后一日数据无法正确设置
具体修复步骤
1. 修正日期比较逻辑
使用moment官方提供的isSame、isBefore、isAfter方法,并指定'day'粒度忽略时分秒,确保日期匹配准确:
- 替换
moment() < moment(...)为moment().isBefore(moment(...), 'day') - 替换
moment() > moment(...)为moment().isAfter(moment(...), 'day') - 替换
moment(...) === moment()为moment(...).isSame(moment(), 'day')
2. 调整initPreviousAndNext调用时机
由于setCurrentIndex是异步操作,不能在initCurrentDay中直接调用initPreviousAndNext,新增依赖currentIndex的useEffect,当索引更新后再初始化前后日数据:
useEffect(() => { if (currentIndex !== undefined && timetables.length > 0) { initPreviousAndNext() } }, [currentIndex, timetables])
3. 修复前后日数据取值逻辑
无需依赖previousIndex/nextIndex状态,直接通过currentIndex计算索引并获取日程,避免异步状态更新导致的取值错误:
function initPreviousAndNext() { if (currentIndex === undefined) return // 设置前一日 setPreviousDay(currentIndex > 0 ? timetables[currentIndex - 1] : undefined) // 设置后一日 setNextDay(currentIndex < timetables.length - 1 ? timetables[currentIndex + 1] : undefined) }
4. 移除冗余状态(可选优化)
previousIndex和nextIndex可通过currentIndex直接计算,无需单独维护,删除这两个状态变量可减少状态同步问题。
修改后的完整代码
import { useState, useEffect } from 'react' import moment from 'moment' export function MainPage() { const [previousDay, setPreviousDay] = useState<ITimetable>() const [currentDay, setCurrentDay] = useState<ITimetable>() const [nextDay, setNextDay] = useState<ITimetable>() const [currentIndex, setCurrentIndex] = useState<number>() const { timetables, loading, error } = useTimetables() useEffect(() => { initCurrentDay() }, [timetables]) useEffect(() => { if (currentIndex !== undefined && timetables.length > 0) { initPreviousAndNext() } }, [currentIndex, timetables]) function initCurrentDay() { if (timetables.length === 0) return const today = moment() const firstDay = moment(timetables[0].date, "dddd - DD/MM/YY") const lastDay = moment(timetables[timetables.length - 1].date, "dddd - DD/MM/YY") if (today.isBefore(firstDay, 'day')) { setCurrentDay(timetables[0]) setCurrentIndex(0) } else if (today.isAfter(lastDay, 'day')) { setCurrentDay(timetables[timetables.length - 1]) setCurrentIndex(timetables.length - 1) } else { const matched = timetables.find(item => { return moment(item.date, "dddd - DD/MM/YY").isSame(today, 'day') }) if (matched) { const index = timetables.indexOf(matched) setCurrentDay(matched) setCurrentIndex(index) } } } function initPreviousAndNext() { if (currentIndex === undefined) return setPreviousDay(currentIndex > 0 ? timetables[currentIndex - 1] : undefined) setNextDay(currentIndex < timetables.length - 1 ? timetables[currentIndex + 1] : undefined) } return ( <> <NavigationPanel></NavigationPanel> { loading && <div>Loading...</div>} { error && <div>{ error }</div>} { timetables.length > 0 && <div className="timetables-view"> <div className="previous-timetable"> {previousDay && <Timetable timetable={ previousDay } key={ previousDay.id }></Timetable> } </div> {currentDay && <div className="current-timetable"> {previousDay && <img src={ arrowLeft } alt="切换到前一日"></img>} <CurrentTimetable timetable={ currentDay } key={ currentDay.id }></CurrentTimetable> {nextDay && <img src={ arrowRight } alt="切换到后一日"></img>} </div> } <div className="next-timetable"> { nextDay && <Timetable timetable={ nextDay } key={ nextDay.id }></Timetable> } </div> </div> } </> ); }
额外说明
调试时currentDay偶尔显示undefined是因为useTimetables加载数据需要时间,页面能显示当日是因为timetables.length > 0的判断会等待数据加载完成后才渲染,此时currentDay已被正确设置。若需支持手动切换前后日,只需在箭头点击事件中修改currentIndex,前后日数据会自动更新。
内容的提问来源于stack exchange,提问作者Kikorik
相关产品推荐
相关产品推荐

