useEffect 中的奇怪行为,clearInterval() 不起作用

问题描述 投票:0回答:1

我的反应代码中有一个奇怪的行为:

在开发(严格)模式下,当间隔设置为 1000 毫秒时,以下组件在清除函数中未成功清除间隔。如果我将间隔更改为稍大的值(例如 1010 毫秒)。组件卸载时可以停止回调。我不知道为什么代码会这样。有人知道为什么它有如此奇怪的行为,我应该如何解决它?

function Timer() {
  const [secondsPassed, setSecondPassed] = useState(0);
  const [rows, setRow] = useState(1);
  const thresold = 60;

  useEffect(() => {
    console.log("effect start");

    function onInterval() {
      setSecondPassed(prevSec => {
        let currentSecond = prevSec + 1;
        console.log(currentSecond);
        if (currentSecond % thresold === 0) {
          setRow(prevRow => prevRow + 1);
        }
        return currentSecond;
      });
    }
    const intervalId = setInterval(onInterval, 1000);
    console.log(`setinterval: ${intervalId}`);
    
    return () => {
      console.log(`clearInterval: ${intervalId}`);
      clearInterval(intervalId);
    }
  }, []);

  return (
    <>
      <div className="box-x" style={{ width: secondsPassed % thresold }}></div>
    </>
  );
}

但是,这个时钟组件在同一个应用程序中工作得很好。

function Clock() {
  const [now, setNow] = useState(new Date());
  
  useEffect(() => {
    const intervalId = setInterval(() => {
      setNow(new Date());
    }, 1000);
    
    return () => {
      clearInterval(intervalId);
    }
  }, []);

  return (<h2 >Time now is {now.getSeconds()}</h2>);
}

如果我改为使用

setTimeout()
setTimeout()
,它也可以在 1000 毫秒内正常工作。

环境:

  • 节点:
    v22.3.0
  • 反应:
    18.3.1
  • vscode:
    1.93.1
  • 铬:
    127.0.6533.120 arm64
javascript reactjs react-hooks
1个回答
0
投票

请查看依赖项数组,它现在是空的。这意味着挂钩内的代码将仅在两个事件上运行 - 组件的安装和卸载。挂钩内的代码将在挂载时运行,而该代码返回的代码将在卸载时运行。这就是clearInterval 现在只能工作一次的原因。然而,这段代码运行良好,并给出了正确的结果。

 useEffect(() => {
    console.log("effect start");
    ...
  }, []);

如果您仍然想了解如何强制执行clearInterval,请参阅下面的方法之一。

步骤:

a) 请向挂钩添加依赖项。

b) onInterval 函数必须移出钩子。 然后将其设置为其依赖项。

c) 现在会发生的是,在每次渲染时,函数 onInterval 将被重新定义并产生一个新的功能对象。

d) 这将导致钩子中的依赖被识别为 改变。

e) 这将导致重新同步操作。

f) 每个重新同步操作都将从执行 返回的函数,然后继续处理钩子的处理程序。

App.js

import { useEffect, useState } from 'react';

export default function Timer() {
  const [secondsPassed, setSecondPassed] = useState(0);
  const [rows, setRow] = useState(1);
  const thresold = 60;

  function onInterval() {
    setSecondPassed((prevSec) => {
      let currentSecond = prevSec + 1;
      console.log(currentSecond);
      if (currentSecond % thresold === 0) {
        setRow((prevRow) => prevRow + 1);
      }
      return currentSecond;
    });
  }

  useEffect(() => {
    console.log('effect start');

    const intervalId = setInterval(onInterval, 1000);
    console.log(`setinterval: ${intervalId}`);

    return () => {
      console.log(`clearInterval: ${intervalId}`);
      clearInterval(intervalId);
    };
  }, [onInterval]);

  return (
    <>
      <div className="box-x" style={{ width: secondsPassed % thresold }}>
        seconds passed : {secondsPassed}
      </div>
    </>
  );
}

试运行

Browser display

© www.soinside.com 2019 - 2024. All rights reserved.