簡體   English   中英

State 內使用效果未更新

[英]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>

所以,我的問題是:

  1. 為什么我當前的代碼不起作用(為什么在useEffectpaused而不更新)?
  2. 有什么方法可以使上述代碼正常工作,以便我可以“暫停”在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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM