React useState — state 变量的作用域

huangapple go评论57阅读模式
英文:

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:

React useState — state 变量的作用域

答案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 &#39;react&#39;;
const ThingToAnimate = (props) =&gt; {
const [userControlled, setUserControlled] = useState(false);
const [animationStep, setAnimationStep] = useState(4);
const animationTimeout = useRef(null);
useEffect(() =&gt; {
goNextStep();
}, []);
useEffect(() =&gt; {
document.addEventListener(&#39;keydown&#39;, keydownHandler);
return () =&gt; {
document.removeEventListener(&#39;keydown&#39;, keydownHandler);
};
}, []);
const keydownHandler = (event) =&gt; {
if (event.keyCode === 13) {
setUserControlled((prevControlled) =&gt; !prevControlled);
if (!userControlled) {
clearTimeout(animationTimeout.current);
}
setAnimationStep(0);
}
};
useEffect(() =&gt; {
if (!userControlled) {
animationTimeout.current = setTimeout(() =&gt; {
goNextStep();
}, 1500);
}
return () =&gt; {
clearTimeout(animationTimeout.current);
};
}, [animationStep, userControlled]);
const goNextStep = () =&gt; {
if (userControlled) {
setAnimationStep(0);
return;
}
const newStep = animationStep &gt; 3 ? 0 : animationStep + 1;
setAnimationStep(newStep);
};
return (
&lt;div&gt;
&lt;h1&gt;Animation Step: {animationStep}&lt;/h1&gt;
&lt;h1&gt;User Controlled: {userControlled ? &#39;true&#39; : &#39;false&#39;}&lt;/h1&gt;
&lt;/div&gt;
);
};
export default ThingToAnimate;

huangapple
  • 本文由 发表于 2023年5月21日 06:57:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/76297631.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定