Can I export values from an arrow function in ReactJS? - reactjs

I'm trying to use pass lng lat variables to the a google maps component from a google's geocode API, however since the geocode function is an arrow function I'm unable to use the values outside of this function. Is there any way to fix this? I need to be able to use the constants lat and lng from the geocode function and pass them to the lat and lang properties in the MapWithMarker constant
Geocode.fromAddress("5th Avenue New York").then(
response => {
const{ lat, lng } = response.results[0].geometry.location;
},
error => {
console.error(error);
}
);
const MapWithAMarker = withGoogleMap(props =>
<GoogleMap
defaultZoom={0}
defaultCenter={{ lat: lat, lng: lng }}
>
<Marker
position={{ lat: lat, lng: lng }}
/>
</GoogleMap>
);
./src/components/dashboard/ProductDetails.js
Line 107: 'lat' is not defined no-undef
Line 107: 'lng' is not defined no-undef
Line 110: 'lat' is not defined no-undef
Line 110: 'lng' is not defined no-undef

One way of doing this is
var MapWithAMarker;
Geocode.fromAddress("5th Avenue New York").then(
response => {
const{ lat, lng } = response.results[0].geometry.location;
MapWithAMarker = withGoogleMap(props =>
<GoogleMap
defaultZoom={0}
defaultCenter={{ lat: lat, lng: lng }}
>
<Marker
position={{ lat: lat, lng: lng }}
/>
</GoogleMap>
);
}
)
.catch(err => console.log(err));
And then you do use it like
if( MapWithAMarker ){
// do whatever you want;
}

You can cache them such as:
const MyMapComponent = function(props) {
const [position, setPosition] = React.useState();
React.useEffect(() => {
Geocode.fromAddress("5th Avenue New York").then(
response => {
setPosition(response.results[0].geometry.location);
},
error => {
console.error(error);
}
);
}, [])
if (!position) {
return null;
}
return <MapWithAMarker {...props} lat={position.lat} lng={position.lng} />
}
const MapWithAMarker = withGoogleMap(props =>
const
<GoogleMap
defaultZoom={0}
defaultCenter={{ lat: props.lat, lng: props.lng }}
>
<Marker
position={{ lat: props.lat, lng: props.lng }}
/>
</GoogleMap>
);
And now you can use MyMapComponent as a decorator of MapWithAMarker.

Related

Geocode in react app not updating the map React

I'm trying to get the map geocode but when the map is loading it never uses the new lat and lng. It's always the original one from the useState.
const CustomMap = () => {
const [customLat, setCustomLat] = useState(0);
const [customLng, setCustomLng] = useState(0);
useEffect(() => {
Geocode.fromAddress("Eiffel Tower").then(
(response) => {
const { lat, lng } = response.results[0].geometry.location;
setCustomLat(lat);
setCustomLng(lng);
},
(error) => {
console.log(error.data);
}
);
}, [])
return (
<GoogleMapReact
bootstrapURLKeys={{ key: 'API_KEY' }}
defaultCenter={{ lat: customLat, lng: customLng }}
defaultZoom={11}>
</GoogleMapReact>
)
}
Even though in the console I can see it's getting the map with url of the Eifell towel.
Fetch finished loading: GET "https://maps.googleapis.com/maps/api/geocode/json?address=Eiffel%20Tower&key=KEY&language=en".
That is the last console log but the center of the map is still the original ones.
You are using the default, this only works on the first render, to update you need use the center, like this
<GoogleMapReact
bootstrapURLKeys={{ key: 'API_KEY' }}
defaultCenter={{ lat: customLat, lng: customLng }}
center = {{lat: customLat, lng: customLng}}
defaultZoom={11}>
</GoogleMapReact>

react-google maps vehicle live tracking

I am trying to move the selected marker with deviceId by fetching data from api endpoint. At first render i am fetching the cordinates data for last 1 minute and pushing the cordinates in an empty array declared as Path and after every 30 seconds I am running a setinterval function which fetches the data of last 30 seconds in every 30 seconds and pushes the cordinates in the Path array. this is the code which can give some idea:
import {
withGoogleMap,
withScriptjs,
GoogleMap,
Polyline,
Marker
} from "react-google-maps";
const mapStyles = require("./mapStyles.json");
const Map = ({deviceId}) => {
const [progress, setProgress]= useState()
const path= []
const getInitialPositions = async () => {
let from = new Date()
let milliseconds = Date.parse(from)
milliseconds = milliseconds - (1* 60 * 1000)
from = new Date(milliseconds).toISOString()
console.log(from)
const to = new Date().toISOString()
console.log(to)
const response = await fetch(`/api/positions/?deviceId=${37}&from=${from}&to=${to}`, {
headers: {
'Accept': 'application/json'
}
})
const items = await response.json()
console.log(items)
items.map( item => {
path.push({lat: item.latitude, lng: item.longitude})
return path
})
console.log(path)
};
const getPositions30Seconds = async () => {
let from = new Date()
let milliseconds = Date.parse(from)
milliseconds = milliseconds - (0.5* 60 * 1000)
from = new Date(milliseconds).toISOString()
console.log(from)
const to = new Date().toISOString()
console.log(to)
const response = await fetch(`/api/positions/?deviceId=14&from=${from}&to=${to}`, {
headers: {
'Accept': 'application/json'
}
})
const items = await response.json()
console.log(items)
items.map( item => {
path.push({lat: item.latitude, lng: item.longitude})
return path
})
console.log(path)
};
useEffect (() => {
const interval = window.setInterval(getPositions30Seconds,30000)
return () => {
window.clearInterval(interval)
}
},[]);
useEffect(()=>{
getInitialPositions()
},[])
const icon = {
url: '/images/icon/car.png',
scaledSize: new window.google.maps.Size(30, 30),
anchor: { x: 10, y: 10 }
};
return (
<GoogleMap
defaultZoom={4}
defaultCenter={path[path.length-1]}
defaultOptions={{ styles: mapStyles, fullscreenControl: false, mapTypeControl: false, streetViewControl: false}}
>
{progress && (
<>
<Polyline
path={progress}
options={{ strokeColor: "light" }}
/>
<Marker
icon={icon}
position={progress[progress.length - 1]}
/>
</>
)}
</GoogleMap>
);
};
const MapComponent = withScriptjs(withGoogleMap(Map))
export default () => (
<MapComponent
googleMapURL={`https://maps.googleapis.com/maps/api/js?key=${process.env.REACT_APP_GOOGLEMAP_KEY}&v=3.exp&libraries=geometry,drawing,places`}
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `100%`, width: '100%' }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
)
I want to use this path array just like below mentioned class component which have a static Path array data.
import React from "react";
import {
withGoogleMap,
withScriptjs,
GoogleMap,
Polyline,
Marker
} from "react-google-maps";
const mapStyles = require("./mapStyles.json");
class Map extends React.Component {
state = {
progress: [],
selectedMarker: false
};
path = [
{ lat: 28.539533333333335, lng: 77.05334444444445},
{lat: 28.539581666666667, lng: 77.05323333333334},
{lat: 28.539614999999998, lng: 77.05313333333334},
{lat: 28.539766666666665, lng: 77.05258166666667},
{lat: 28.539884444444443, lng: 77.05252666666667},
{lat: 28.539884444444443, lng: 77.05252666666667},
{lat: 28.542425, lng: 77.05253666666667},
{lat: 28.544408333333333, lng: 77.05254333333333},
{lat: 28.544445, lng: 77.052655},
{lat: 28.544383333333332, lng: 77.05419333333333},
{lat: 28.544383333333332, lng: 77.05419333333333},
{lat: 28.544383333333332, lng: 77.05419333333333},
{lat: 28.544383333333332, lng: 77.05419333333333},
{lat: 28.544383333333332, lng: 77.05419333333333},
{lat: 28.544439999999998, lng: 77.05512},
{lat: 28.544561666666667, lng: 77.055295},
{lat: 28.546363333333336, lng: 77.05680833333334},
{lat: 28.54712166666667, lng: 77.05741277777777},
{lat: 28.547226666666667, lng: 77.05737},
{lat: 28.54752166666667, lng: 77.05704},
{lat: 28.54752166666667, lng: 77.05704},
{lat: 28.54752166666667, lng: 77.05704},
{lat: 28.54752166666667, lng: 77.05704},
{lat: 28.54752166666667, lng: 77.05704},
{lat: 28.547706666666667, lng: 77.05692833333333},
{lat: 28.548081666666665, lng: 77.05644666666666},
{lat: 28.548235000000002, lng: 77.05629},
{lat: 28.548235000000002, lng: 77.05629},
{lat: 28.548571666666668, lng: 77.05574333333333},
{lat: 28.548655, lng: 77.05571166666667},
{lat: 28.548745, lng: 77.05563666666667},
{lat:28.55049, lng: 77.05438},
{lat: 28.550714999999997, lng: 77.05413666666666},
{lat: 28.55175, lng: 77.05356833333333},
{lat: 28.553496666666668, lng: 77.05223166666667},
{lat: 28.553915, lng: 77.05173833333333 }
];
velocity = 50;
initialDate = new Date();
getDistance = () => {
// seconds between when the component loaded and now
const differentInTime = (new Date() - this.initialDate) / 8000; // pass to seconds
return differentInTime * this.velocity;
};
componentDidMount = () => {
this.interval = window.setInterval(this.moveObject, 100);
};
handleClick = (marker, event) => {
// console.log({ marker })
this.setState({ selectedMarker: marker })
}
componentWillUnmount = () => {
window.clearInterval(this.interval);
};
moveObject = () => {
const distance = this.getDistance();
if (!distance) {
return;
}
let progress = this.path.filter(
coordinates => coordinates.distance < distance
);
const nextLine = this.path.find(
coordinates => coordinates.distance > distance
);
if (!nextLine) {
this.setState({ progress });
return; // it's the end!
}
const lastLine = progress[progress.length - 1];
const lastLineLatLng = new window.google.maps.LatLng(
lastLine.lat,
lastLine.lng
);
const nextLineLatLng = new window.google.maps.LatLng(
nextLine.lat,
nextLine.lng
);
// distance of this line
const totalDistance = nextLine.distance - lastLine.distance;
const percentage = (distance - lastLine.distance) / totalDistance;
const position = window.google.maps.geometry.spherical.interpolate(
lastLineLatLng,
nextLineLatLng,
percentage
);
progress = progress.concat(position);
this.setState({ progress });
};
componentWillMount = () => {
this.path = this.path.map((coordinates, i, array) => {
if (i === 0) {
return { ...coordinates, distance: 0 }; // it begins here!
}
const { lat: lat1, lng: lng1 } = coordinates;
const latLong1 = new window.google.maps.LatLng(lat1, lng1);
const { lat: lat2, lng: lng2 } = array[0];
const latLong2 = new window.google.maps.LatLng(lat2, lng2);
// in meters:
const distance = window.google.maps.geometry.spherical.computeDistanceBetween(
latLong1,
latLong2
);
return { ...coordinates, distance };
});
console.log(this.path);
};
componentDidUpdate = () => {
const distance = this.getDistance();
if (!distance) {
return;
}
let progress = this.path.filter(
coordinates => coordinates.distance < distance
);
const nextLine = this.path.find(
coordinates => coordinates.distance > distance
);
let point1, point2;
if (nextLine) {
point1 = progress[progress.length - 1];
point2 = nextLine;
} else {
// it's the end, so use the latest 2
point1 = progress[progress.length - 2];
point2 = progress[progress.length - 1];
}
const point1LatLng = new window.google.maps.LatLng(point1.lat, point1.lng);
const point2LatLng = new window.google.maps.LatLng(point2.lat, point2.lng);
const angle = window.google.maps.geometry.spherical.computeHeading(
point1LatLng,
point2LatLng
);
const actualAngle = angle - 35;
const markerUrl =
'/images/icon/car.png'
const item = document.querySelector(`[src="${markerUrl}"]`);
if (item) {
// when it hasn't loaded, it's null
item.style.transform = `rotate(${actualAngle}deg)`;
}
};
render = () => {
const icon = {
url: '/images/icon/car.png',
scaledSize: new window.google.maps.Size(35, 35),
anchor: { x: 10, y: 10 }
};
return (
<GoogleMap
defaultZoom={18}
defaultCenter={{lat: 28.539766666666665, lng: 77.05258166666667}}
defaultOptions={{
styles: mapStyles,
fullscreenControl: false,
mapTypeControl: false,
streetViewControl: false,
}}
>
{this.state.progress && (
<>
<Polyline
path={this.state.progress}
options={{ strokeColor: "gray" }}
/>
<Marker
icon={icon}
position={this.state.progress[this.state.progress.length - 1]}
/>
</>
)}
</GoogleMap>
);
};
}
const MapComponent = withScriptjs(withGoogleMap(Map));
export default () => (
<MapComponent
googleMapURL={`https://maps.googleapis.com/maps/api/js?key=${process.env.REACT_APP_GOOGLEMAP_KEY}&v=3.exp&libraries=geometry,drawing,places`}
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `100%`, width: '100%' }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
)
It works fine(with static data) and the marker moves smoothly but .I want to implement this dynamically for every devices on click. I am posting this because I am stuck as I am new to react . I am finding hooks a bit easy so tried with functional component but failing to do it. stuck since 5 days and now i am helpless. Somebody help me to implement this like a charm.

Limit the number of clicks on map using React

I am using the Google Maps API and I want to limit the number of markers on the map (limit: 10). I couldn't find anything related to it in the API docs
neither I can find any similar source to solve my problem.
Here is my code:
import React from "react";
import {
GoogleMap,
useLoadScript,
Marker,
} from "#react-google-maps/api";
const mapContainerStyle = {
height: "50vh",
width: "100vw",
};
const options = {
zoomControl: false,
scrollwheel: false,
draggable: false
};
const center = {
lat: 34.155834,
lng: -119.202789,
};
export default function App() {
const { isLoaded, loadError } = useLoadScript({
googleMapsApiKey: "AIzaSyCpaQDSgGTCetTR0uz42RyV80cByaGaYLs",
});
const [markers, setMarkers] = React.useState([]);
const onMapClick =
React.useCallback((e) => {
setMarkers((current) =>
[
...current,
{
lat: e.latLng.lat(),
lng: e.latLng.lng(),
},
]);
}, []);
const mapRef = React.useRef();
const onMapLoad = React.useCallback((map) => {
mapRef.current = map;
}, []);
if (loadError) return "Error";
if (!isLoaded) return "Loading...";
return (
<div>
<GoogleMap
id="map"
mapContainerStyle={mapContainerStyle}
zoom={14}
center={center}
options={options}
onClick={onMapClick}
onLoad={onMapLoad}
>
{markers.map((marker) => (
<Marker
key={`${marker.lat}-${marker.lng}`}
position={{ lat: marker.lat, lng: marker.lng }}
onClick={() => {
console.log("clicked")
}}
/>
))}
</GoogleMap>
</div>
);
}
How do I set the number of clicks up to 10?
You could do something like this. You may also want to call another function before returning current to perform some other update to alert the user they are maxed out on markers.
const onMapClick = React.useCallback((e) => {
setMarkers((current) => {
if (current.length < 10) {
return [
...current,
{
lat: e.latLng.lat(),
lng: e.latLng.lng()
}
];
} else {
return current;
};
});
}, []);

React with Google Maps Api, how to recenter map

I'm using react and using the map as a functional component. (tbh I'm still unsure as to when to use classes v. functions when it comes to classes). however my main issue is that I'm using the google Maps API to present a map and I'm trying to center a map on the users current location. also, I wanted it to update as they walked around so I was just going to use a set interval function to set a timer of when it updates.
I thought that the navigator would be my best bet. Although I can't seem to find a proper function to update the center property after initialization.
I'll mark where I think the function should go.
Here's the documentation I've been looking at:
https://tomchentw.github.io/react-google-maps/#googlemap
function MapContainer() {
const [currentLoc,setCurrentLoc] = useState(
{
lat: 42.331429,
lng: -83.045753
}
)
function setLocation() {
if(navigator.geolocation)
{
navigator.geolocation.getCurrentPosition(
(position) => {
setCurrentLoc(position.coords.latitude,position.coords.longitude)
}
)
}
}
return (
<LoadScript
googleMapsApiKey="Api key"
>
<GoogleMap
//THIS IS WHERE YOU STYLLLLLEEEE
//also where you set what is visible with the controls
options= {{
styles:mapStyles['hide'],
mapTypeControl:false,
disableDefaultUI:true,
draggable:true,
zoomControl:true
}}
id="44b929060bf5f087"
mapContainerStyle=
{{
height: "86.5vh",
width: "100%",
stylers: mapStyles['hide'],
draggable: false
}}
center={{
lat: 44.331429,
lng: -83.045753
}}
zoom={10}
>
{
setInterval((props) => {
var long;
var lati;
if(navigator.geolocation)
{
navigator.geolocation.getCurrentPosition(
(position) => {
lati = position.coords.latitude;
long = position.coords.longitude;
}
)
};
//here is where i'd set the center if i had a function i could do it with
}, 2000)
}
</GoogleMap>
</LoadScript>
)
}
export default MapContainer;
I can't access the documentation link of react-google-maps library. You can use the #react-google-maps/api library since this is a rewrite of the react-google-maps and is more maintained.
For your use case, you can set the value of your center in a state and update it in the setinterval function. This way, each time the state value of the center changes, the center also changes. Please see this sample code and code snippet below:
import React from "react";
import { GoogleMap, LoadScript } from "#react-google-maps/api";
const containerStyle = {
width: "400px",
height: "400px",
};
function MyComponent() {
const [map, setMap] = React.useState(null);
const [currentLoc, setCurrentLoc] = React.useState({
lat: 42.331429,
lng: -83.045753,
});
const onLoad = React.useCallback(function callback(map) {
setMap(map);
setInterval((props) => {
console.log("refreshed");
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((position) => {
setCurrentLoc({
lat: position.coords.latitude,
lng: position.coords.longitude,
});
});
}
}, 2000);
}, []);
return (
<LoadScript googleMapsApiKey="YOUR_API_KEY">
<GoogleMap
mapContainerStyle={containerStyle}
center={{ lat: currentLoc.lat, lng: currentLoc.lng }}
zoom={10}
onLoad={onLoad}
>
{/* Child components, such as markers, info windows, etc. */}
<></>
</GoogleMap>
</LoadScript>
);
}
export default React.memo(MyComponent);

google.maps.places.PlacesService(map) always returns null

Am trying to fetch nearby restaurants using google maps API with react-google-maps.
import React from 'react';
import { compose, withState, withProps, withStateHandlers, lifecycle } from 'recompose';
import { withScriptjs, withGoogleMap, GoogleMap, Marker, InfoWindow } from 'react-google-maps';
import dotenv from 'dotenv';
import { HOME_MAP } from './MapNavigationConstants';
import MapSearch from './MapSearch';
dotenv.config();
const MapWithInfoWindow = compose(
withProps({
googleMapURL: HOME_MAP,
loadingElement: <div style={{ height: `100%` }} />,
containerElement:<div style={{ height: `720px` }} />,
mapElement:<div style={{ height: `100%` }} />,
}),
withState('mapUrl', 'setMapUrl', ''),
withState('bounds', 'setBounds', null),
withState('center', 'setCenter', {
lat: 53.3498, lng: -6.2603
}),
withState('markers', 'setMarkers', [
{
position: {
lat: () => 53.3498,
lng: () => -6.2603
}
}
]),
withState('places', 'updatePlaces', ''),
withStateHandlers(() => ({
isOpen: false,
isExploreOn: false,
}), {
onToggleOpen: ({ isOpen }) => () => ({
isOpen: !isOpen,
})
}
),
lifecycle({
componentWillMount() {
const refs = {}
this.setState({
onMapMounted: ref => {
refs.map = ref;
},
onBoundsChanged: (bounds, center, markers) => {
this.props.setBounds(!bounds ? this.props.bounds : bounds);
this.props.setCenter(!center ? this.props.center : center);
this.props.setMarkers(!markers ? this.props.markers : markers);
},
fetchPlaces: () => {
this.props.setMapUrl('places');
const bounds = refs.map.getBounds();
const map = refs.map;
const service = new window.google.maps.places.PlacesService(map);
console.log(service);
const request = {
bounds,
type: ['restaurants', 'cafe','bars']
};
service.nearBySearch(request, (results, status) => {
if (status === window.google.maps.places.PlacesServiceStatus.OK) {
this.props.updatePlaces(results);
}
});
}
})
},
}),
withScriptjs,
withGoogleMap)((props) =>
<GoogleMap
ref={props.onMapMounted}
defaultCenter = {props.center}
defaultZoom = { 13 }
center={props.center}
bounds={props.bounds}
options={{gestureHandling: 'cooperative',
scrollwheel: false,
disableDefaultUI: true,
}}
bootstrapURLKeys={{libraries: props.mapUrl}}
onBoundsChanged={props.onBoundsChanged}
>
<MapSearch
onBoundsChanged={(bounds, center, markers) => props.onBoundsChanged(bounds, center, markers)}
fetchPlaces={props.fetchPlaces}
/>
{
props.markers && props.markers.length > 0 && props.markers.map((marker, index) => (
<Marker
key={index}
position={{ lat: marker.position.lat(), lng:marker.position.lng() }}
onClick={props.onToggleOpen}
>
{props.isOpen && <InfoWindow onCloseClick={props.onToggleOpen}>
{props.children}
</InfoWindow>}
</Marker>
))
}{
props.places && props.places.length > 0 && props.places.map((place, index) => (
<Marker
key={index}
position={{ lat: place.location.lat(), lng:place.location.lng() }}
onClick={props.onToggleOpen}
>
{props.isOpen && <InfoWindow onCloseClick={props.onToggleOpen}>
{props.children}
</InfoWindow>}
</Marker>
))
}
</GoogleMap>
)
export default MapWithInfoWindow;
here HOME_MAP = https://maps.googleapis.com/maps/api/js?key=${KEY}&v=3.exp&libraries=geometry,drawing,places
Inside fetchplaces method, new window.google.maps.places.PlacesService(map) always returns null and service.nearBySearch throws not a function error.
Please help.
There are at least two issues with your example
const map = refs.map;
const service = new window.google.maps.places.PlacesService(map);
^^^
map object here corresponds to the instance of Map component while google.maps.places.PlacesService class expects Google Maps object instead. In case of react-google-maps library PlacesService could be instantiated like this:
mapMounted(element) {
const mapObject = element.context[MAP];
const service = new google.maps.places.PlacesService(map);
//...
}
where
<GoogleMap
ref={this.mapMounted}
defaultZoom={this.props.zoom}
defaultCenter={this.props.center}/>
There is also a typo at line:
service.nearBySearch(request, (results, status) => {
^^^^^^^^^^^^
function should be renamed to nearbySearch
Here is a demo that demonstrates how to utilize Places Service with react-google-maps library.

Resources