React中setInterval更新状态后获取最终状态异常问题咨询
问题原因分析
这是React里典型的闭包陷阱问题!
当你点击按钮调用resettimer()时,setInterval的回调函数会捕获当前resettimer作用域里的stringstate值。而React的状态更新是异步的,并且每次调用setStringstate后,组件会重新渲染,但已经创建的setInterval回调并不会更新它捕获的stringstate——它始终拿着resettimer刚执行时的那个旧状态值,所以当timesrun达到randomtime执行else分支时,打印的自然是旧值。
解决方法
我给你两种简单可行的方案,你可以根据需求选择:
方案一:直接保存最后生成的随机字符串(最简便)
既然我们在每次定时器触发时都生成了新的thisString,完全可以在resettimer里用一个变量保存最后一次的结果,不需要依赖state来获取最终值:
function resettimer() { var timesrun = 0; var randomtime = Math.floor(Math.random() * 15) + 10; let finalHexagram; // 保存最后一次生成的卦象字符串 const interval = setInterval(() => { timesrun += 1; const thisString = getRandomHexagram(); if (timesrun < randomtime) { finalHexagram = thisString; setStringstate(thisString); console.log("the current permutation is: " + thisString); } else { clearInterval(interval); // 直接用保存的finalHexagram,而不是state console.log("the final state is: " + finalHexagram); // 这里可以直接用finalHexagram调用其他函数 // otherFunction(finalHexagram); } }, 100); }
方案二:使用useRef跟踪最新状态
如果你确实需要从状态中获取最新值,可以用useRef来保存最新的stringstate,因为ref的current属性是可变的,不会被闭包固定:
首先在组件里声明ref:
function App() { const [stringstate, setStringstate] = useState("VVVVVV"); const stringRef = useRef(stringstate); // 声明ref // 监听state变化,更新ref的current值 useEffect(() => { stringRef.current = stringstate; }, [stringstate]); function resettimer() { var timesrun = 0; var randomtime = Math.floor(Math.random() * 15) + 10; const interval = setInterval(() => { timesrun += 1; if (timesrun < randomtime) { const thisString = getRandomHexagram(); setStringstate(thisString); console.log("the current permutation is: " + thisString); } else { clearInterval(interval); // 用ref的current获取最新状态 console.log("the final state is: " + stringRef.current); // otherFunction(stringRef.current); } }, 100); } // ... 其余代码不变 }
总结
方案一最直接,因为我们本来就有每次生成的字符串,没必要绕到state里拿;方案二更适合需要随时获取最新state的场景。两种方法都能解决你遇到的旧状态问题~
内容的提问来源于stack exchange,提问作者erikksuzuki
相关产品推荐
相关产品推荐

