Search code examples
javascriptreact-nativegoogle-places-apisetstatereact-native-maps

React-native-maps setState in getCurrentPosition does not work for my url API


I am working on a lifecycle of States. I'm new to React-native and React globally.

I'm trying to display markers from GooglePlacesAPI on the map but my API's URL is invalid. When I log this URL it appears that latitude and longitude are 'null'.

First, I tried to implement the function getCurrentPosition in componentDidMount() where I was setting states "lat" and "lng", and after, my axios's function which use the URL, but my states were null.

So next, I tried to use callback's function. But I get an error : "[Unhandled promise rejection: TypeError: Cannot read property 'setState' of undefined]"

Here's my code :

import * as React from 'react';
import { StyleSheet } from 'react-native';
import { ScrollView } from 'react-native-gesture-handler';
import MapView  from 'react-native-maps';
import axios from 'axios';

var API_KEY= '###################';
var GOOGLE_PLACES_URL = 'https://maps.googleapis.com/maps/api/place/nearbysearch/json';


class MapScreen extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
          isloading: null,
          lat: null,
          lng: null,
          error:null,
          markers: [],
        };
      }    

    componentDidMount() {
        navigator.geolocation.getCurrentPosition(
            (position) => {
              console.log(position);
              this.setState({
                lat: position.coords.latitude,
                lng: position.coords.longitude,
                error: null,
              }, () => {
                  getMarkers(this.state.lat, this.state.lng)
              });
            },(error) => this.setState({ error: error.message }),
            { enableHighAccuracy: false, timeout: 200000, maximumAge: 1000 },
          );
// My first try was to put the axios.get(url) here.
   } 

function getMarkers(lat, lng){
    const url = 
`${GOOGLE_PLACES_URL}?location=${lat},${lng}&radius=1500&keyword=grow+shop&key=${API_KEY}`;
   axios.get(url)
      .then(res =>
        res.data.results.map(marker => ({
          latitude: `${marker.geometry.location.latitude}`,
          longitude: `${marker.geometry.location.longitude}`,
          name: `${marker.name}`,
          isOpen: `${marker.opening_hours.open_now}`,
          adress: `${marker.vicinity}`
      })))
      .then(markers => {
      console.log(url)
      console.log(markers)
      this.setState({
       markers,
       isloading: false
      })
      })
      .catch(error => this.setState({error, isloading: true}));  
  }

  render() {
    const { markers, lat, lng } = this.state
    return (
        this.state.lat !== null && <MapView style={styles.map} initialRegion={{
        latitude:this.state.lat,
        longitude:this.state.lng,
        latitudeDelta: 1,
        longitudeDelta: 1,
       }}
       showsUserLocation= {true}>
       {markers.map(marker => {
        return (<MapView.Marker 
      coordinate = {{
          latitude:this.state.lat,
          longitude:this.state.lng
      }}/>)
      })}
       </MapView>
        )}
  }



 export default MapScreen;

When I log 'Position' in 'getCurrentPosition' :

 Object {
"coords": Object {
  "accuracy": 65,
  "altitude": 11.887725830078125,
  "altitudeAccuracy": 10,
  "heading": -1,
  "latitude": 44.83189806318307,
  "longitude": -0.5747879551813496,
  "speed": -1,
},
"timestamp": 1589450273414.4202,
}

The map is working because the Initial Region is center on my location.

Maybe should I create file 'utils/getCurrentPosition' where I could use React Context to set latitude and longitude's user ?

I heard that getCurrentPosition was 'async' and I think it's for this reason that my first try was a failure.

EDIT : Finally I retrieve expected results from the APi so my callback function is working, I just need to figure out how to fill my state 'markers' with the data. I will post my code when everything working fine.


Solution

  • So everything is working fine now.

    The callback function is perfect for this kind of problem.

    Here is my code to solve it :

    class MapScreen extends React.Component {
        constructor(props) {
            super(props);
            this.getMarkers = this.getMarkers.bind(this);
            this.state = {
              isloading: true,
              lat: null,
              lng: null,
              error:null,
              markers: [],
            };
          }    
           getMarkers(lat, lng){
            const url = `${GOOGLE_PLACES_URL}?location=${lat},${lng}&radius=1500&keyword=grow+shop&key=${API_KEY}`;
           fetch(url)
              .then(res => res.json())
              .then((data) => {
                this.setState({ markers: data.results });
              })
              .catch(error => this.setState({error, isloading: true}));  
          }
        componentDidMount() {
            navigator.geolocation.getCurrentPosition(
                (position) => {
                  console.log(position);
                  this.setState({
                    lat: position.coords.latitude,
                    lng: position.coords.longitude,
                    error: null }, () => {
                      this.getMarkers(this.state.lat, this.state.lng);
                  });
                },(error) => this.setState({ error: error.message }),
                { enableHighAccuracy: true, timeout: 200, maximumAge: 1000 },
              );
       } 
    
    
    
       render() {
         const { markers } = this.state
        return (
            this.state.lat !== null && <MapView style={styles.map} initialRegion={{
            latitude:this.state.lat,
            longitude:this.state.lng,
            latitudeDelta: 1,
            longitudeDelta: 1,
           }}
           showsUserLocation= {true}
           >
           {this.state.markers !== null && this.state.markers.map(marker => (
            <MapView.Marker
          coordinate = {{
              latitude:marker.geometry.location.lat,
              longitude:marker.geometry.location.lng
          }}>
          </MapView.Marker>
           ))}
           </MapView>
            )}
      }
    

    I was not handling the JSON's response well.

    Now I do. I just have to attribute 'Key prop' to each prop and it's good.