Где я иду не так, используя крючки / состояние с геолокации в реагировать родной? - PullRequest
0 голосов
/ 04 апреля 2020

У меня есть компонент, который должен регистрировать долготу / широту / отметку времени через регулярные интервалы.

Когда пользователь нажимает кнопку START, должно начаться отслеживание. Когда пользователь нажимает кнопку STOP, отслеживание должно прекратиться.

Чтобы реализовать это, я построил следующее (я начинающий реагировать и JS, так что это может быть совершенно неправильным способом) :

const Tracking = props => {

  const [currentLatitude, setCurrentLatitude] = useState(0);
  const [currentLongitude, setCurrentLongitude] = useState(0);
  const [currentTimestamp, setCurrentTimestamp] = useState(0);

  const [buttonTitle, setButtonTitle] = useState('Start');
  const [isTracking, setIsTracking] = useState(false);
  var getLocationInterval;


  function getLocation() {
    navigator.geolocation.getCurrentPosition(
      position => {
        setCurrentLongitude(position.coords.longitude);
        setCurrentLatitude(position.coords.latitude);
        setCurrentTimestamp(position.timestamp);
      },
      error => alert(error.message),
      { enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 }
    );
    console.log(currentTimestamp, currentLatitude, currentLongitude);
  };

  function startStop() {
    if(!isTracking){
      //START
      setIsTracking(true);
      getLocationInterval = setInterval(getLocation, 500);
      setButtonTitle('Stop');
    }
    else{
      //STOP
      setIsTracking(false);
      clearInterval(getLocationInterval);
      setButtonTitle('Start');
    }
  };


  return (
    <View style={{width: '100%', height: '100%'}}>
      <MapView showsUserLocation style={{flex: 1}} />
      <MenuButton title = {buttonTitle} onPress={startStop}/>
    </View>
  );

}

Ожидаемое поведение : после нажатия кнопки START текст кнопки меняется на STOP. И в моей консоли я начинаю получать выходные данные каждые 500 мс с последней лат / длинной / меткой времени. При нажатии кнопки STOP текст кнопки меняется на START, и выходы останавливаются.

Фактическое поведение : после нажатия кнопки START текст кнопки корректно меняется на STOP, но только в начальные состояния ( 0) повторно выводятся. Когда я затем нажимаю STOP, следующая широта / долгота / отметка времени начинает повторно выводиться на консоль. 0 также все еще выводятся, потому что интервал, похоже, не останавливается.

Я предполагаю, что я просто использую здесь состояние совершенно неправильно. Пожалуйста, кто-нибудь может мне помочь?

1 Ответ

2 голосов
/ 04 апреля 2020

Итак, есть несколько проблем в том, как вы настроили его с точки зрения логики c. Я считаю, что я исправил все проблемы, которые я видел в вашем коде ниже.

Причина, по которой печатался только 0, заключается в том, что у currentLatitude / currentLongitude / currentTimestamp, на который ссылалась функция getLocation, было закрытие вокруг после запуска интервала не будет sh обновляться до новой версии функции, которая создавалась при каждом повторном рендеринге. Чтобы устранить это, я удалил ссылки на эти переменные из функции getLocation в сделанных мною изменениях.

Причина, по которой все еще выводились 0, заключается в том, что для getLocationInterval было установлено значение undefined в каждом рендере. Вам бы пришлось сделать это либо ссылкой, либо переменной состояния, чтобы это оставалось между повторными рендерингами.

Я переместил logi c из функции startStop в хук useEffect, так как это правильный способ добиться эффекта изнутри компонента крючка. Это также позволяет объединить все логи c в одном прямом месте, которое можно легко извлечь в пользовательский хук, если вам нужно использовать его в других местах.

const Tracking = props => {
const [currentLatitude, setCurrentLatitude] = useState(0);
const [currentLongitude, setCurrentLongitude] = useState(0);
const [currentTimestamp, setCurrentTimestamp] = useState(0);

const [isTracking, setIsTracking] = useState(false);

useEffect(() => {
    if (!isTracking) return;
    function getLocation() {
    navigator.geolocation.getCurrentPosition(
        position => {
        setCurrentLongitude(position.coords.longitude);
        setCurrentLatitude(position.coords.latitude);
        setCurrentTimestamp(position.timestamp);
        console.log(
            position.coords.longitude,
            position.coords.latitude,
            position.timestamp
        );
        },
        error => alert(error.message),
        { enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 }
    );
    }
    let getLocationInterval = setInterval(getLocation, 500);
    return () => clearInterval(getLocationInterval);
}, [isTracking]);

return (
    <View style={{ width: '100%', height: '100%' }}>
    <MapView showsUserLocation style={{ flex: 1 }} />
    <MenuButton
        title={isTracking ? 'Stop' : 'Start'}
        onPress={() => {
        setIsTracking(!isTracking);
        }}
    />
    </View>
);
};

Редактировать: Объяснение return from useEffect:

Функция возврата в строке 27 приведенного выше кода: return () => clearInterval(getLocationInterval);

clearInterval запускается не сразу, поскольку является частью объявления функции стрелки. Мы возвращаем эту функцию, а не вызываем ее напрямую. После того, как мы вернем объявление функции, activ теперь снова контролирует код и вызывает его, когда захочет.

В частности, useEffect позволяет вам вернуть функцию, которая используется для очистки. И он вызывает эту функцию для компонента, размонтирующего или непосредственно перед тем, как он снова запускает ловушку (т. Е. Массив зависимостей изменился)

В официальной документации реагирования есть немного больше информации: https://reactjs.org/docs/hooks-reference.html#cleaning -up-an -эффект

...