为什么我无法清除我的 setInterval?

Why I'm not able to clear my setInterval?

我正在尝试创建一个 自动计数器 当组件(例如主页)在视图中呈现时。计数器应从 1 开始并在 10 停止。但我无法在 10 时清除我的 setInterval。计数器保持递增。 有什么问题?

非常感谢。

export const Home = () => {

    const [counter, setCounter] = useState(1)
    let timer = useRef()


    const animate = () => {
        if(counter === 10){
          clearInterval(timer.current)
        }else{
          setCounter((previous) => previous + 1) 
        }
    }


    useEffect(() => {
        timer.current = window.setInterval(() => {
            animate()
        }, 900)
    
     }, [])



  return (
      <div style={{textAlign: "center"}}>
         <h1>{counter}</h1>
      </div>
  )
}

问题是 animate 函数在 counter 的初始值上的闭包,即 1。结果,条件 counter === 10 永远不会计算为真。

这就是为什么你永远不应该在 useEffect 钩子的依赖性问题上撒谎。这样做可能会导致错误,因为关闭了组件先前渲染中的陈旧值。

解决方案

您可以去掉 animate() 函数并在 useEffect 钩子中编写逻辑。

function App() {
  const [counter, setCounter] = React.useState(1);
  const counterRef = React.useRef(counter);

  React.useEffect(() => {
    const id = setInterval(() => {
      // if "counter" is 10, stop the interval
      if (counterRef.current === 10) {
        clearInterval(id);
      } else {
        // update the "counter" and "counterRef"
        setCounter((prevCounter) => {
          counterRef.current = ++prevCounter;
          return prevCounter;
        });
      }
    }, 900);

    // clearn interval on unmount
    return () => clearInterval(id);
  }, []);

  return (
    <div style={{ textAlign: "center" }}>
      <h1>{counter}</h1>
    </div>
  );
}

ReactDOM.render(<App/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>

那是因为修改 ref.current 不会触发重新渲染。 您需要使用useEffect清除它。

 const animate = () => {
    setCounter((previous) => previous + 1);
  };

  useEffect(() => {
    if (counter === 10) clearInterval(timer.current);
  }, [counter]);

我会创建第二个 useEffect() 来清除间隔:

const { useEffect, useRef, useState } = React;

const Home = () => {
  const [counter, setCounter] = useState(1);
  const timer = useRef();

  useEffect(() => {
    timer.current = setInterval(() => {
      setCounter(x => x + 1);
    }, 900);

    // in case the component unmounts before the timer finishes
    return () => clearInterval(timer.current);
  }, []);

  useEffect(() => {
    if (counter === 10) {
      clearInterval(timer.current);
    }
  }, [counter]);

  return (
    <div style={{ textAlign: 'center' }}>
      <h1>{counter}</h1>
    </div>
  );
}

ReactDOM.render(<Home/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>