如何在lodash filter中引用先前state解决定时器选值重复问题
问题根因
这是React Hooks典型的陈旧闭包问题:
- 你当前的
useEffect没有声明依赖,只会在组件首次挂载时执行一次,setInterval的回调函数从此就一直捕获着首次渲染时的panelRight初始值,永远拿不到后续更新后的状态。 - 每次定时器触发时,过滤菜单用的
panelRight都是同一个初始值,可选菜单池一直固定,自然会反复赋值同一个随机元素,逻辑不符合预期。
修复方法
优先用函数式更新的方案,不会打乱定时器的计时节奏,也不用额外处理依赖:
const [panelRight, setPanelRight] = useState(sample(filter(menu, (el) => el !== panelLeft))); useEffect(() => { const interval = setInterval(() => { // 函数式setState的入参就是上一次更新后的最新状态,不存在闭包陈旧问题 setPanelRight(prevRight => { const availableMenu = filter(menu, (el) => el !== panelLeft && el !== prevRight); return sample(availableMenu); }); }, 10000); // 组件卸载时必须清除定时器,避免内存泄漏 return () => clearInterval(interval); }, [panelLeft]); // 如果panelLeft是动态变化的就保留在依赖里,是固定常量的话可以留空数组
如果你不想用函数式更新,也可以把所有响应式依赖加入useEffect的依赖数组,每次状态变化时重建定时器,保证回调捕获到最新值:
const [panelRight, setPanelRight] = useState(sample(filter(menu, (el) => el !== panelLeft))); useEffect(() => { const interval = setInterval(() => { const availableMenu = filter(menu, (el) => el !== panelLeft && el !== panelRight); setPanelRight(sample(availableMenu)); }, 10000); return () => clearInterval(interval); }, [panelLeft, panelRight]);
这个方案的缺陷是每次
panelRight更新都会重置10秒的计时周期,如果你的需求是严格每10秒切换一次,不受状态更新影响,就选第一种函数式更新的方案。
内容的提问来源于stack exchange,提问作者Malloc28
相关产品推荐
相关产品推荐

