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

Cut*_*ter 4 javascript react-native react-hooks

我有一个屏幕组件,它有一个getPosition()按时间间隔每秒调用一次的函数。

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

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

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

当我调用该stopRace()函数,然后按返回时,间隔被清除。我知道这一点是因为我的控制台记录:

Still Running
Still Running
Still Running
Reached cleanup function
Run Code Online (Sandbox Code Playgroud)

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

Still Running
Still Running
Still Running
Reached cleanup function
Still Running
Run Code Online (Sandbox Code Playgroud)

随后是包含以下建议的内存泄漏警告:

To fix, cancel all subscriptions and asynchronous tasks in %s.%s, a useEffect cleanup function
Run Code Online (Sandbox Code Playgroud)

这正是我想要做的,但由于某种超出我理解的原因而不起作用。

这是该组件的相关代码:

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)
    }
Run Code Online (Sandbox Code Playgroud)

为什么stopRace()函数可以正确清除间隔,但cleanup()函数却不能?

Zac*_*ber 7

您的代码可能无法正常工作的部分原因是,如果您运行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);
  }
};
Run Code Online (Sandbox Code Playgroud)