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

时间:2020-04-25 23:59:47

标签: javascript react-native react-hooks

我有一个具有getPosition()功能的屏幕组件,该功能每秒间隔调用一次。

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

为此,我尝试将间隔ID存储在raceUpdateInterval状态变量中。

然后我使用clearInterval(raceUpdateInterval)函数和stopRace()函数中的cleanup()清除此间隔。

当我调用stopRace()函数时,然后按back,将清除间隔。我知道这是因为我的控制台记录了:

Still Running
Still Running
Still Running
Reached cleanup function

但是,如果按返回按钮,则间隔不会清除。而是我的控制台日志:

Still Running
Still Running
Still Running
Reached cleanup function
Still Running

紧接着是包含以下建议的内存泄漏警告:

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()函数可以正确清除间隔,而cleanup()函数却不能清除间隔?

2 个答案:

答案 0 :(得分:1)

您的代码可能无法正常运行的部分原因是,如果您多次运行startRace函数而不在其间停止,则间隔将再次启动,但间隔ID会重新开始已经迷路了。

无法清除的主要原因是,当useEffect带有[]作为依赖项数组时,它在开始时看到的raceUpdateInterval是:0。之所以看不到更新后的值,是因为useEffect在运行(和重新运行)的点创建了一个封闭值。因此,您需要使用引用来为其提供对raceUpdateInterval最新版本的访问权限

这是我修改您的代码以使其正常运行的方式。不用在函数中启动计时器,而使用useEffect来启动该副作用,这样就永远不会出现计时器无法清除的情况。

我使用ref将函数添加到间隔中,因为我不知道getPosition函数中有多少个关闭变量。这样,positionFunctRef.current始终指​​向该函数的最新版本,而不是保持静态。

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);
  }
};

答案 1 :(得分:1)

componentWillUnmount用于清理(例如删除事件侦听器,取消计时器等)。假设您要在componentDidMount中添加事件监听器,并在componentWillUnmount中将其删除,如下所示。

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)
        }
      }, []);