javascript React 5秒计时器崩溃

vm0i2vca  于 2023-01-16  发布在  Java
关注(0)|答案(1)|浏览(162)

我试着理解我的错误是什么。我在useEffect中使用setInterval[]依赖项更新ms(毫秒)状态,每10毫秒加1。我有一个time()函数,负责更新mssecs状态,还负责停止和显示计时器。计时器达到5秒后,done状态设置为true,间隔被清除,计时器应停止,但它却崩溃并显示以下错误:“* 重新呈现太多。React限制呈现次数以防止无限循环 *"。为什么会发生这种情况?如何解决?以下是代码链接https://codepen.io/Montinyek/pen/zYLzBZP?editors=1111

function App() {
  const [secs, setSecs] = React.useState(0);
  const [ms, setMs] = React.useState(0);
  const [done, setDone] = React.useState(false)
  
  let id = React.useRef()
  
  React.useEffect(() => {
    id.current = setInterval(() => {
      if (!done) {
        setMs(prev => prev += 1)
      } 
}, 10);
    return () => clearInterval(id.current);
  }, [])
  
  function time() {
    if (ms === 100) {
    setMs(0)
    setSecs(prev => prev += 1)
    }
    if (secs === 5) {
    clearInterval(id.current)
    setDone(true)
    }
    let formattedSecs = secs < 10 ? "0" + secs : secs;
    let formattedMils = ms < 10 ? "0" + ms : ms;
    return `${formattedSecs} : ${formattedMils}`;
  }

  return <div>{time()}</div>;
}
waxmsbnn

waxmsbnn1#

问题是你在 render 中调用了函数time(),而这个函数正在调用set state。通常,你不应该在render中设置state,或者你在渲染时进入了循环状态,状态被设置(这会触发重新渲染),然后状态被再次设置,然后重新渲染,依此类推。
你的问题实际上并不在于新的时间间隔被创建了。它实际上在某种程度上与计时器的滴答声完全无关。问题是当它达到5秒时,它进入了一个“渲染循环”。
具体来说,你的情况是这样的:
1.计时器达到5秒。
1.渲染调用time()

  1. clearInterval(id.current)被调用,setDone(true)也被调用。这里的set操作导致另一个呈现。
    1.渲染调用time()
    1.回到(3)。
    您需要将设置状态的逻辑封装在间隔处理程序中,而不是使您的逻辑与渲染过程内在地链接。(在一个时间间隔内处理状态),因为在调用过时状态时会遇到各种各样的问题。你需要阅读DanAbramov(React的一个关键贡献者)的article about this,我已经从他的博客中删除了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]);
}

function App() {
  const [secs, setSecs] = React.useState(0);
  const [ms, setMs] = React.useState(0);
 
  
  useInterval(() => {
      if (ms >= 100) {
        setMs(0)
        setSecs(prev => prev + 1)
        return
      }

      setMs(prev => prev + 1)
  }, secs < 5 ? 10 : null)
  
  function getFormattedTime() {
    let formattedSecs = secs < 10 ? "0" + secs : secs;
    let formattedMils = ms < 10 ? "0" + ms : ms;
    return `${formattedSecs} : ${formattedMils}`;
  }
 

  return <div>{getFormattedTime()}</div>;
}

ReactDOM.render(<App />, document.getElementById("root"));

请注意,渲染现在只调用不涉及状态的getFormattedTime
重构时,我发现不需要done,因为useInterval支持通过传递一个变量tickrate来有条件地轻松停止间隔:secs < 5 ? 10 : null . null表示“已停止”。

相关问题