clearInterval () не работает в функциональном компоненте React Native - PullRequest
0 голосов
/ 26 апреля 2020

У меня есть экранный компонент, который имеет функцию getPosition(), вызываемую каждую секунду с интервалом.

Если вызывается функция stopRace() или если пользователь нажимает физическую / графическую кнопку возврата, я хочу очистить этот интервал, чтобы он не продолжал работать в фоновом режиме.

Для этого я попытался сохранить идентификатор интервала в переменной состояния raceUpdateInterval.

I затем очистите этот интервал, используя clearInterval(raceUpdateInterval) в функции stopRace() и функции cleanup().

Когда я вызываю функцию stopRace(), затем нажимаю назад, интервал очищается. Я знаю это, потому что моя консоль регистрирует:

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 ]

1 голос
/ 26 апреля 2020

componentWillUnmount используется для очистки (например, удаление прослушивателей событий, отмена таймера и т. Д. c). Допустим, вы добавляете прослушиватель событий в 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)
        }
      }, []);
1 голос
/ 26 апреля 2020

Частично причина того, что ваш код мог работать не так, как раньше, заключается в том, что если вы запустили функцию startRace более одного раза, не останавливая ее в промежутке, интервал запустился бы снова, но идентификатор интервала бы был потерян.

Основная причина, по которой он не смог очистить, заключается в том, что raceUpdateInterval, который он увидел в начале, когда useEffect с [] в качестве массива зависимостей видел: 0. Причина, по которой он не увидел обновленные значения, заключается в том, что useEffect создает замыкание для значений в точке, в которой он работает (и повторно запускается). Поэтому вам нужно использовать ссылку, чтобы предоставить ей доступ к последней версии raceUpdateInterval

Вот как я могу изменить ваш код, чтобы он работал правильно. Вместо запуска таймера в функции используйте useEffect, чтобы запустить этот побочный эффект таким образом, что никогда не будет случая, когда таймер не сможет очистить.

Я добавил функцию к интервалу, используя ссылка, потому что я не знаю, сколько переменных замыкания есть в функции getPosition. Таким образом, positionFunctRef.current всегда указывает на последнюю версию функции, а не на оставшиеся данные c.

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