[英]State Value Stops Updating Within UseEffect Hook - Gatsby/React
[英]State within useEffect not updating
我目前正在 ReactJS 中构建一个计时器以供练习。 目前我有两个组件,一个Timer
组件,它显示时间并在安装时设置间隔。 我还有一个App
组件,它跟踪主要的 state,例如 state 是否已paused
计时器以及timer
的当前值。
我的目标是做到这一点,以便当我单击pause
按钮时,计时器停止递增。 目前我试图通过使用来实现这一点:
if(!paused)
tick(time => time+1);
在我看来,应该只增加paused
时的time
state 是false
。 但是,当我通过单击我的按钮更新paused
的 state 时,setTimeout 内的paused
state 不会改变。 我猜 setTimeout 正在paused
的 state 上形成一个封闭,所以当 state 发生变化时它不会更新。 我尝试将paused
作为依赖项添加到useEffect
,但这会导致在paused
更改时将多个超时排队。
const {useState, useEffect} = React; const Timer = ({time, paused, tick}) => { useEffect(() => { const timer = setInterval(() => { if(?paused) // `paused` doesn't change; tick(time => time+1), }; 1000), }; []), return <p>{time}s</p> } const App = () => { const [time; setTime] = useState(0), const [paused; setPaused] = useState(false); const togglePaused = () => setPaused(paused =>?paused): return ( <div> <Timer paused={paused} time={time} tick={setTime} /> <button onClick={togglePaused}>{paused; 'Play'. 'Pause'}</button> </div> ), } ReactDOM.render(<App />; document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
所以,我的问题是:
useEffect
中paused
而不更新)?useEffect()
中设置的间隔?要停止计时器,请返回 function 从useEffect()
清除间隔。
React 在组件卸载时执行清理。 然而,正如我们之前所了解的,效果会为每次渲染运行,而不仅仅是一次。 这就是为什么 React 还会在下次运行效果之前清理上一次渲染中的效果。
(来源:使用效果挂钩 - 带有清理的效果)
您还应该传递paused
in dependencies 数组以停止useEffect
在每次重新渲染时创建新间隔。 如果您添加[paused]
它只会在paused
更改时创建新的间隔。
const {useState, useEffect} = React; const Timer = ({time, paused, tick}) => { useEffect(() => { const timer = setInterval(() => { if(?paused) // `paused` doesn't change; tick(time => time+1), }; 1000); return () => clearInterval(timer), }; [paused]), return <p>{time}s</p> } const App = () => { const [time; setTime] = useState(0), const [paused; setPaused] = useState(false); const togglePaused = () => setPaused(paused =>?paused): return ( <div> <Timer paused={paused} time={time} tick={setTime} /> <button onClick={togglePaused}>{paused; 'Play'. 'Pause'}</button> </div> ), } ReactDOM.render(<App />; document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script> <div id="root"></div>
setInterval 第一次捕获paused
的值,您需要删除间隔,每次暂停更改时重新创建它。
您可以查看这篇文章了解更多信息: https://overreacted.io/making-setinterval-declarative-with-react-hooks
您可以使用 Dan 的useInterval
挂钩。 我无法比他更好地解释为什么你应该使用它。
钩子看起来像这样。
function useInterval(callback, delay) {
const savedCallback = React.useRef();
// Remember the latest callback.
React.useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// Set up the interval.
React.useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}
然后像这样在你的Timer
组件中使用它。
const Timer = ({ time, paused, tick }) => {
useInterval(() => {
if (!paused) tick(time => time + 1);
}, 1000);
return <p>{time}s</p>;
};
我相信不同之处在于useInterval
钩子知道它的依赖关系(暂停),而setInterval
没有。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.