繁体   English   中英

clearInterval() 在 React Native 功能组件中不起作用

[英]clearInterval() not working in React Native functional component

我有一个屏幕组件,它有一个getPosition() function 每秒调用一个间隔。

如果stopRace() function 或者如果用户按下物理/图形后退按钮,我想清除此间隔,使其不会继续在后台运行。

为此,我尝试将区间 ID 存储在raceUpdateInterval state 变量中。

然后我在stopRace() function 和cleanup() function 中使用clearInterval(raceUpdateInterval)清除此间隔。

当我调用stopRace() function,然后按回,间隔被清除。 我知道这一点是因为我的控制台日志:

Still Running
Still Running
Still Running
Reached cleanup function

但是,如果我按下后退按钮,则间隔不会清除。 相反,我的控制台记录:

Still Running
Still Running
Still Running
Reached cleanup function
Still Running

随后是 memory 泄漏警告,其中包含以下建议:

To fix, cancel all subscriptions and asynchronous tasks in %s.%s, a useEffect cleanup function

这正是我想要做的,但由于某种超出我理解的原因无法正常工作。

以下是该组件的相关代码:

const RaceScreen = ({route, navigation}) => {

    const [raceUpdateInterval, setRaceUpdateInterval] = useState(0);

    useEffect(function() {
        return function cleanup() {
            console.log('Reached cleanup function')
            clearInterval(raceUpdateInterval)
        }
      }, []);

    function getPosition(){
        console.log("Still being called")
        //get position
    }

    function startRace(){
        setRaceUpdateInterval(setInterval(getPosition, 1000))
    }

    function stopRace(){
        clearInterval(raceUpdateInterval)
    }

为什么stopRace() function 正确清除间隔但cleanup() function 没有?

您的代码可能无法按原样运行的部分原因是,如果您多次运行startRace function 而不在其间停止它,则间隔将再次启动,但间隔 ID 会丢失。

它未能清除的主要原因是,当使用 [] 作为依赖数组的 useEffect 看到时,它在开始时看到的 raceUpdateInterval 是: 0 它没有看到更新值的原因是因为 useEffect 在它运行(和重新运行)的点创建了一个闭包。 所以你需要使用一个引用来让它访问最新版本的 raceUpdateInterval

这是我将如何修改您的代码以使其正常工作的方法。 与其在 function 中启动计时器,不如使用useEffect启动该副作用,这样就永远不会出现计时器无法清理的情况。

我使用 ref 将 function 添加到区间,因为我不知道 getPosition function 中有多少闭包变量。 这样 positionFunctRef.current 总是指向最新版本的 function 而不是剩余的 static。

const RaceScreen = ({ route, navigation }) => {
  const [runningTimer, setRunningTimer] = useState(false);
  function getPosition() {
    console.log('Still being called');
    //get position
  }
  const positionFunctRef = useRef(getPosition);
  useEffect(() => {
    positionFunctRef.current = positionFunctRef;
  });

  useEffect(
    function () {
      if (!runningTimer) {
        return;
      }

      const intervalId = setInterval(() => {
        positionFunctRef.current();
      }, 1000);
      return () => {
        console.log('Reached cleanup function');
        clearInterval(intervalId);
      };
    },
    [runningTimer]
  );

  function startRace() {
    setRunningTimer(true);
  }

  function stopRace() {
    setRunningTimer(false);
  }
};

componentWillUnmount用于清理(如删除事件侦听器、取消计时器等)。 假设您在componentWillUnmount中添加一个事件侦听器,并在componentDidMount中删除它,如下所示。

componentDidMount() {
  window.addEventListener('mousemove', () => {})
}

componentWillUnmount() {
  window.removeEventListener('mousemove', () => {})
}

上面代码的钩子等效项如下

useEffect(() => {
  window.addEventListener('mousemove', () => {});

  // returned function will be called on component unmount 
  return () => {
    window.removeEventListener('mousemove', () => {})
  }
}, [])

所以你更好的代码是:

 useEffect(function() {
  setRaceUpdateInterval(setInterval(getPosition, 1000))
        return function cleanup() {
            console.log('Reached cleanup function')
            clearInterval(raceUpdateInterval)
        }
      }, []);

不要在 state 中存储间隔 id 之类的东西,因为每次更新都会重新渲染。 如果您功能正常,请使用setInterval实现useRef() ,如果基于 class ,请使用this.interval

另一个问题是在 ref 的功能组件中调用clearInterval() ,而不是.current

这是我刚刚调试的一个片段:

  const spinnerCount = useRef(0)
  const interval = useRef(null)

  useEffect(() => {
    if (withProgress && inProgress && notification == '') {
      interval.current = setInterval(() => {
        if (spinnerCount.current >= 40) {
          clearInterval(interval.current)
          spinnerCount.current = 0
          setNotification('Something happened... Please try again.')
        } else {
          spinnerCount.current = spinnerCount.current + 1
        }
      }, 1000)
    } 
    if (notification !== '' && inProgress === false) {
      const delay = notification.length > 100 ? 6000 : 3000
      setTimeout(() => {
        clearInterval(interval.current)
        spinnerCount.current = 0
        setNotification('');
      }, delay);
    }
  }, [inProgress])

里面有一些额外的东西,基本上这是一个消失的通知组件,它还具有一个进度微调器。 在这种情况下,如果组件正在显示微调器,但从未触发成功/错误通知,则微调器将在 40 秒后自动退出。 因此间隔/微调器计数

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM