我希望组件每5秒重新渲染一次并在相应的索引处显示该数组,已知数组长度恰好是10。
这是我到目前为止所做的:
const GameCard = ({ gameArray, startGame }) => {
const [arrayIndex, setArrayIndex] = useState(0);
let {questionWord} = gameArray[arrayIndex] ;
useEffect(() => {
if(!startGame) return;
let timer = setTimeout(() => {
if(arrayIndex === 9) return; //is this valid ?
setArrayIndex(arrayIndex +1)} , 1500)
return () => {
clearTimeout(timer)
}
}, [arrayIndex ,startGame]);
return (<div>{questionWord}</div>)
startGame
是一个布尔属性,用户点击后就会更改。
现在,这是可行的,但是您可以看到即时消息基于2个变量触发了useEffect,当我到达数组末尾时,我将在setTimeout内部返回,以防止arrayIndex更新。
这感觉很hack,如何改善异步useEffect? 从setTimeout函数或useEffect返回时会发生什么?
答案 0 :(得分:1)
像setArrayIndex((i) => i + 1)
这样使用functional updates可能很诱人:
如果新状态是使用先前状态计算的,则可以将函数传递给setState。
但是,您的代码需要使用arrayIndex
常量的原始值,即,如果在注册0
时它是setTimeout
,则希望它是{{1 }},甚至在5秒钟后。
您的代码中已经存在这种情况-在不同的渲染器中它会有所不同(每个计时器将具有不同的值,因为它是在不同的渲染器中注册的),但是该值在计时器的注册和执行之间不会改变(参见Closures)。
0
,如下所示:
arrayIndex === 9
此外,如果您希望计时器减少渲染时间(即,每次渲染后不是5秒,而是5秒间隔),则需要mutable reference而不是不可变状态:
useEffect(() => {
if(!startGame || arrayIndex >= 9) return
const timer = setTimeout(() => {
setArrayIndex(arrayIndex + 1)
}, 5000)
return () => clearTimeout(timer)
}, [arrayIndex, startGame])