React中setInterval始终使用useState默认值的问题排查
React中setInterval无法获取最新state的原因及解决方法
问题原因
你的useEffect依赖数组为空,这意味着它只会在组件首次渲染时执行一次。setInterval的回调函数形成了闭包,捕获的是组件首次渲染时所有state变量的初始值(比如FilterType默认是3)。后续用户操作修改state时,组件会重新渲染,但这个旧的interval回调不会被更新,依然持有首次渲染时的state值,所以API调用永远用的是默认值。
解决方案
方法一:将用到的state变量加入useEffect依赖数组
每当依赖的state变量变化时,useEffect会重新执行,清除旧的interval并创建新的interval,新的回调就能捕获到最新的state值。
修改后的代码:
useEffect(() => { const interval = setInterval(() => { if (FilterType === 1){ LoadAnnualStats(Year) } else if (FilterType === 2){ LoadMonthlyStats(`${MonthYear}-${Month}`) } else if (FilterType === 3){ LoadDayStats(CurrentDate) } else if (FilterType === 4){ LoadCustomStats(FromDate, ToDate) } else if (FilterType === 5){ LoadDayStats(yesterday) } }, 10000) return () => clearInterval(interval); }, [FilterType, Year, MonthYear, Month, CurrentDate, FromDate, ToDate, yesterday])
注意要把所有在回调中用到的变量都加入依赖数组,包括yesterday(如果它是动态变化的)。
方法二:用useRef保存最新state引用
创建useRef对象同步最新的state值,让interval回调通过ref的current属性访问最新值,避免频繁销毁重建interval。
步骤如下:
- 创建对应state的ref对象
- 同步state变化到ref
- 在interval回调中使用ref.current获取最新值
代码示例:
// 1. 创建ref对象 const filterRef = useRef(FilterType); const yearRef = useRef(Year); const monthYearRef = useRef(MonthYear); const monthRef = useRef(Month); const currentDateRef = useRef(CurrentDate); const fromDateRef = useRef(FromDate); const toDateRef = useRef(ToDate); // 2. 同步state到ref useEffect(() => { filterRef.current = FilterType; yearRef.current = Year; monthYearRef.current = MonthYear; monthRef.current = Month; currentDateRef.current = CurrentDate; fromDateRef.current = FromDate; toDateRef.current = ToDate; }, [FilterType, Year, MonthYear, Month, CurrentDate, FromDate, ToDate]) // 3. interval中使用ref.current useEffect(() => { const interval = setInterval(() => { const currentFilter = filterRef.current; if (currentFilter === 1){ LoadAnnualStats(yearRef.current) } else if (currentFilter === 2){ LoadMonthlyStats(`${monthYearRef.current}-${monthRef.current}`) } else if (currentFilter === 3){ LoadDayStats(currentDateRef.current) } else if (currentFilter === 4){ LoadCustomStats(fromDateRef.current, toDateRef.current) } else if (currentFilter === 5){ LoadDayStats(yesterday) } }, 10000) return () => clearInterval(interval); }, [yesterday])
这种方法适合state变化频繁的场景,避免了每次state变化都重建interval的开销。
内容的提问来源于stack exchange,提问作者Armughan Ahmad
相关产品推荐
相关产品推荐

