代码之家  ›  专栏  ›  技术社区  ›  MattonRoi

为什么在react useEffect中设置间隔,然后在间隔函数中清除它没有效果

  •  0
  • MattonRoi  · 技术社区  · 3 月前

    我有这个组件:

    function App() {
      const deployIntervalRef = React.useRef(null);
      
      React.useEffect(() => {
        deployIntervalRef.current = setInterval(() => {
          console.log("interval handler");
          console.log(deployIntervalRef.current);
          clearInterval(deployIntervalRef.current);
          deployIntervalRef.current = null;
        },2000);
      }, []);
      
      return <div></div>;
    }
    
    ReactDOM.createRoot(document.getElementById('root')).render(<React.StrictMode><App/></React.StrictMode>);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.min.js"></script>
    <div id="root"/>

    在我看来,它应该输出一次间隔处理程序,然后停止,但它没有。为什么 从第二个日志中,您可以看到当清除时,ref有一个间隔id

    1 回复  |  直到 3 月前
        1
  •  2
  •   Brian Thompson    3 月前

    StrictMode 导致您 useEffect 运行两次,这反过来又导致创建第二个间隔。

    问题是在第二个间隔可以被清除之前,ref被重新分配为null。所以你的第二个音程是无限的。

    问题演示

    function App() {
      console.log('rendered')
      const deployIntervalRef = React.useRef(null);
      
      React.useEffect(() => {
        deployIntervalRef.current = setInterval(() => {
          console.log("interval handler");
          console.log(deployIntervalRef.current);
          clearInterval(deployIntervalRef.current);
          deployIntervalRef.current = null;
        },2000);
          console.log(deployIntervalRef.current);
      }, []);
      
      return <div></div>;
    }
    
    ReactDOM.createRoot(document.getElementById('root')).render(<React.StrictMode><App/></React.StrictMode>);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.min.js"></script>
    <div id="root"/>

    不带StrictMode的演示

    function App() {
      console.log('rendered')
      const deployIntervalRef = React.useRef(null);
      
      React.useEffect(() => {
        deployIntervalRef.current = setInterval(() => {
          console.log("interval handler");
          console.log(deployIntervalRef.current);
          clearInterval(deployIntervalRef.current);
          deployIntervalRef.current = null;
        }, 2000);
          console.log(deployIntervalRef.current);
      }, []);
      
      return <div></div>;
    }
    
    ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
    <script src=“https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.min.js“></script>
    <script src=“https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.min.js“></script>
    <div id=“root”/>

    重要提示

    “为什么”会发生这种行为的答案是 StrictMode ,但这绝不是建议您从应用程序中删除严格模式。

    相反,您应该使用另一种方法来清除间隔。这正是事情的类型 使用效果 cleanup函数是为而设计的。

    useEffect(() => {
      const interval = setInterval(() => {
        // your code
      },2000);
    
      return () => clearInterval(interval);
    }, []);
    

    另一种选择(取决于您的实际用例)是使用超时而不是间隔。您当前的代码只需等待2秒钟即可运行回调,然后再也不会运行。如果您只需要运行一次,请使用 setTimeout .

    useEffect(() => {
      setTimeout(() => {
        // your code
      }, 2000);
    }, []);