![](/img/trans.png)
[英]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.