英文:
React useState — scope of state variable
问题
下面是您提供的代码的中文翻译部分:
我有一个关于在哪里可以使用useState
创建的状态的范围的疑惑,我正在尝试澄清。
在下面的代码示例中,我有一个包含0、1、2、3的状态之间的4步动画循环。
动画在每个步骤停留1500秒。useEffect
监听animationStep
的变化,并设置一个1500秒的超时(存储在ref中),然后调用goToNextStep
,它会将步骤递增到3,然后回到0。那部分都有效。(您可以在下面的代码片段底部看到这个功能)。
我的下一个要求是,如果用户按回车键,动画应该停止并重新启动。这应该是简单的开/关切换,如果在用户关闭动画时动画正在进行中,步骤应该回到0,动画应该停止。我试图通过清除超时并设置另一个名为userControlled
的状态来实现这一点。
然而,当我将它设置为true时,我的userControlled
状态似乎无法保持。我不确定这是否与键盘按键在事件范围而不是组件范围内调用有关?
我对React不太熟悉,不知道我在这里漏掉了什么,或者是否使用useRef
来在该组件重新渲染之间存储超时对象的方法是正确的(?)
无论如何,当我多次按回车键时,我希望在“userControlled为false”和“userControlled为true”之间切换,但它总是显示“userControlled为false”。
请注意,我仅提供代码的翻译,没有回答您的翻译问题。如果您有其他问题或需要进一步的帮助,请随时提出。
英文:
I have a confusion I'm trying to clear about the scope of where I can use a state that I created with useState
In the code example below, I have a 4-step animation cycle between states denoted with 0,1,2,3.
The animation sits at each step for 1500 seconds. A useEffect
looks at changes to animationStep
, and sets a timeout (which gets stored to a ref) of 1500 seconds before calling goToNextStep
, which increments the step up to 3 and then back to 0. That part all works. (You see that functional towards the bottom of the code snippet below).
My next requirement is to have the animation stop & start if the user presses the return key. THis should simply toggle on/off, and if an animation is in the middle of animating while the user turns it off, the step should go back to 0 and the thing should stop animating. I am trying to accomplish this by clearing out the timeout and setting another state called userControlled
However, my userControlled
state doesn't seem to stick when I set it to true. I'm not sure if this has to do with the fact that keydown gets called in the scope of the event and not the component?
I'm not quite savvy enough with React to know what I'm missing here, or if using a useRef
is the right way to store the timeout object between re-renders of this component.(?)
Either way, when I hit the return key several times, I am expecting to toggle between saying "userControled is false" and "userControlled is true" but it just always says "userControlled is false"
import React, { useState, useEffect, useRef } from 'react'
const ThingToAnimate = (props) => {
const [userControlled, setUserControlled] = useState(false)
const [animationStep, setAnimationStep] = useState(4)
var animationTimeout = useRef(null);
useEffect(() => {
goNextStep()
}, [])
useEffect(() => {
document.addEventListener("keydown", (event) => {
keydownHandler(event);
});
}, [])
const keydownHandler = (event) => {
console.log(event.keyCode);
console.log("userControlled is", userControlled);
if(event.keyCode === 13) {
if(userControlled) {
console.log("auto control")
setUserControlled(false);
} else {
console.log("setting user controlled")
setUserControlled(true);
}
}
}
useEffect(() => {
setAnimationStep(0)
}, [userControlled])
const goNextStep = () => {
const newStep = (animationStep > 3) ? 0 : animationStep + 1;
setAnimationStep(newStep)
}
useEffect(() => {
if (!userControlled) {
animationTimeout = setTimeout(() => {
goNextStep()
}, 1500)
}
}, [animationStep])
return (
// render code omitted; render is based on the current value of animationStep
)
}
results in my browser if I hit return key several times:
答案1
得分: 1
由于您正在使用useRef
钩子来存储超时ID,您应该通过current
属性来访问它,useRef
钩子返回一个带有current
属性的对象,可以用来在渲染之间存储可变值。
import React, { useState, useEffect, useRef } from 'react';
const ThingToAnimate = (props) => {
const [userControlled, setUserControlled] = useState(false);
const [animationStep, setAnimationStep] = useState(4);
const animationTimeout = useRef(null);
useEffect(() => {
goNextStep();
}, []);
useEffect(() => {
document.addEventListener('keydown', keydownHandler);
return () => {
document.removeEventListener('keydown', keydownHandler);
};
}, []);
const keydownHandler = (event) => {
if (event.keyCode === 13) {
setUserControlled((prevControlled) => !prevControlled);
if (!userControlled) {
clearTimeout(animationTimeout.current);
}
setAnimationStep(0);
}
};
useEffect(() => {
if (!userControlled) {
animationTimeout.current = setTimeout(() => {
goNextStep();
}, 1500);
}
return () => {
clearTimeout(animationTimeout.current);
};
}, [animationStep, userControlled]);
const goNextStep = () => {
if (userControlled) {
setAnimationStep(0);
return;
}
const newStep = animationStep > 3 ? 0 : animationStep + 1;
setAnimationStep(newStep);
};
return (
<div>
<h1>Animation Step: {animationStep}</h1>
<h1>User Controlled: {userControlled ? 'true' : 'false'}</h1>
</div>
);
};
export default ThingToAnimate;
请注意,上述代码是您提供的代码的翻译部分,不包括其他信息。
英文:
Since you're using a useRef
hook to store the timeout ID, you should access it via the current
property, the useRef
hook returns an object with a current
property that can be used to store mutable values across renders.
import React, { useState, useEffect, useRef } from 'react';
const ThingToAnimate = (props) => {
const [userControlled, setUserControlled] = useState(false);
const [animationStep, setAnimationStep] = useState(4);
const animationTimeout = useRef(null);
useEffect(() => {
goNextStep();
}, []);
useEffect(() => {
document.addEventListener('keydown', keydownHandler);
return () => {
document.removeEventListener('keydown', keydownHandler);
};
}, []);
const keydownHandler = (event) => {
if (event.keyCode === 13) {
setUserControlled((prevControlled) => !prevControlled);
if (!userControlled) {
clearTimeout(animationTimeout.current);
}
setAnimationStep(0);
}
};
useEffect(() => {
if (!userControlled) {
animationTimeout.current = setTimeout(() => {
goNextStep();
}, 1500);
}
return () => {
clearTimeout(animationTimeout.current);
};
}, [animationStep, userControlled]);
const goNextStep = () => {
if (userControlled) {
setAnimationStep(0);
return;
}
const newStep = animationStep > 3 ? 0 : animationStep + 1;
setAnimationStep(newStep);
};
return (
<div>
<h1>Animation Step: {animationStep}</h1>
<h1>User Controlled: {userControlled ? 'true' : 'false'}</h1>
</div>
);
};
export default ThingToAnimate;
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论