Hi i am using vanilla map js in react but in this functional based component marker is not showing but in class based component it works. But i want to use it in functional based component. Please help me. In this functional based component i am using react useEfect.
const GoogleMap = () => {
// const [googleMap, setGoogleMap] = useState();
// const [marker, setMarker] = useState();
let googleMapRef = React.createRef();
let map, marker;
// }
useEffect(() => {
const googleMapScript = document.createElement("script");
googleMapScript.src = `https://maps.googleapis.com/maps/api/js?key=AIzaSyDEkGijSUn59Urd96tftpKsizYGqkUaMFQ`;
window.document.body.appendChild(googleMapScript);
googleMapScript.addEventListener("load", () => {
map = createGoogleMap();
marker = createMarker();
});
}, [])
console.log("Marker", marker)
// componentDidMount() {
// const googleMapScript = document.createElement("script");
// googleMapScript.src = `https://maps.googleapis.com/maps/api/js?key=AIzaSyDjjx0ffXrvMyW4V8Y3Wlk-e2c2H2jAcFw`;
// window.document.body.appendChild(googleMapScript);
// googleMapScript.addEventListener("load", () => {
// this.googleMap = this.createGoogleMap();
// this.marker = this.createMarker();
// });
// }
let createGoogleMap = () => {
new window.google.maps.Map(googleMapRef.current, {
zoom: 16,
center: {
lat: 43.642567,
lng: -79.387054,
},
// disableDefaultUI: true,
});
}
let createMarker = () => {
const marker = new window.google.maps.Marker({
position: { lat: 43.642567, lng: -79.387054 },
map: map,
})
return marker
}
return (
<div
id="google-map"
ref={googleMapRef}
style={{ height: '75vh', width: "100%" }}
/>
)
}
export default GoogleMap;```
Related
i am getting error mapRef.current.getBounds is not a function and map.getBounds is not a function when i want to getBounds of map using useRef react hook
this is my code for getting data from the csv file display marker on the map and when user scroll and zoom map then load more data from csv file and display on the marker based on change latitude and longitude of the map but i am getting error
import React, { useState, useEffect, useRef } from "react";
import { GoogleMap, LoadScript, Marker } from "#react-google-maps/api";
import Papa from "papaparse";
const Map = () => {
const [markers, setMarkers] = useState([]);
const mapRef = useRef(null);
const center = {
lat: 49.87099818,
lng: -97.28513314,
};
useEffect(() => {
const fetchData = async () => {
const response = await fetch("/data/geocoords.csv");
const data = await response.text();
const parsedData = Papa.parse(data, { header: true });
const initialMarkers = parsedData.data
.filter((data) => {
const lat = parseFloat(data.latitude);
const lng = parseFloat(data.longitude);
return mapRef.current.getBounds().contains({ lat, lng });
})
.map((data) => ({
position: {
lat: parseFloat(data.latitude),
lng: parseFloat(data.longitude),
},
title: data.title,
address: data.address,
}));
setMarkers(initialMarkers);
};
fetchData();
}, []);
const handleBoundsChanged = () => {
const map = mapRef.current;
const bounds = map.getBounds();
const ne = bounds.getNorthEast();
const sw = bounds.getSouthWest();
const lat1 = ne.lat();
const lng1 = ne.lng();
const lat2 = sw.lat();
const lng2 = sw.lng();
Papa.parse("/data/geocoords.csv", {
download: true,
header: true,
complete: (results) => {
const newMarkers = results.data
.filter((data) => {
const lat = parseFloat(data.latitude);
const lng = parseFloat(data.longitude);
return (
lat >= lat2 &&
lat <= lat1 &&
lng >= lng2 &&
lng <= lng1 &&
!markers.some((marker) => marker.street === data.street)
);
})
.map((data) => ({
position: {
lat: parseFloat(data.latitude),
lng: parseFloat(data.longitude),
},
title: data.street,
}));
setMarkers([...markers, ...newMarkers]);
},
});
};
return (
<LoadScript googleMapsApiKey={"my_api_key"}>
<GoogleMap
ref={mapRef}
onBoundsChanged={handleBoundsChanged}
mapContainerStyle={{ height: "100vh", width: "100%" }}
zoom={10}
center={center}
>
{markers.map((marker, index) => (
<Marker key={index} position={marker.position} title={marker.title} />
))}
</GoogleMap>
</LoadScript>
);
};
export default Map;
this is the screen shot of my error that i'm getting in google chrome console
Please any one can solve my problem i am stuck on this from last two days thanks in advance
Try to check whether the mapRef.current. has the getBounds() method before using it in the useEffect, try this in the useEffect return statement
return mapRef.current?.getBounds()?.contains({ lat, lng });
I have a component in my react application that uses maps but the map is not loading how can I solve this problem
import React, { useEffect, useMemo } from 'react';
import { MarkerClusterer } from '#googlemaps/markerclusterer';
const Map = () => {
const locations = useMemo(
() => [
{ lat: -31.56391, lng: 147.154312 },
{ lat: -33.718234, lng: 150.363181 },
{ lat: -33.727111, lng: 150.371124 },
],
[],
);
useEffect(() => {
const script = document.createElement('script');
script.src = `https://maps.googleapis.com/maps/api/js?key=${process.env.REACT_APP_GOOGLE_API_KEY}`;
script.async = true;
script.defer = true;
document.body.appendChild(script);
script.onload = () => {
if (typeof window.google !== 'undefined' && window.google.maps) {
const map = new window.google.maps.Map(document.getElementById('map'), {
zoom: 3,
center: { lat: -28.024, lng: 140.887 },
});
const infoWindow = new window.google.maps.InfoWindow({
content: '',
disableAutoPan: true,
});
// Create an array of alphabetical characters used to label the markers.
const labels = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
// Add some markers to the map.
const markers = locations.map((position, i) => {
const label = labels[i % labels.length];
const marker = new window.google.maps.Marker({
position,
label,
});
// markers can only be keyboard focusable when they have click listeners
// open info window when marker is clicked
marker.addListener('click', () => {
infoWindow.setContent(label);
infoWindow.open(map, marker);
});
return marker;
});
// Add a marker clusterer to manage the markers.
MarkerClusterer.factory({ markers, map });
}
};
}, [locations]);
return <div id='map' />;
};
export default Map;
this is my build i want to collect markers with cluster but i fail
enter image description here
in console i see these errors
![Topluluk Tarafından Doğrulandı simgesi](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAQAAAAngNWGAAABDUlEQVR4AYXRgUZDYRjH4TegFTKgpEqiFJgoWAoMEQGBgBboChaaAKxLKAhAhQqAdAmpBIQolkCFqp2nITvNKXuA7+/Hhzey5OWjE4Nq3rzY1f9/NGHPB549492+8Ww060iCS2XdctZdI3GsECmb+HJoIX6x6EgDm+lURTH+YB7V9nAqE5WNme4YKuOiY6iMe6PaQxUUIuTbswgFVNJwA8sO3Bn6yR6bWZMSNtJwDtuWfHpQxaPx9C9zadil7jrCigbq6UXceNIVKTWUIqypm2ytJdTiNyNeXclF6GttOVfeDEc7qzjR23r3OMFqZKng1kw0mXGLrfibHTScOZWgGv9TdC6ROFeMTgwYiIxvJzMRWQbeGZUAAAAASUVORK5CYII=)
I want the markers to appear in clusters on the map, but even the map does not appear
*Trying to show a chat history with infinite reload similar to Skype or any popular chat app
In a chat page. If my chat messages limit is 10 messages.
And the chat has 30.
It will show latest 10 when I load the chat.
When I scroll to the top I want to see the previous 10.
I tried this myself and the scroll position stays the same but the messages load in the view.
It should load to the top and hold the scroll position.
How can this be done?
Here's my page:
https://pastebin.com/36xZPG1W
import React, { useRef, useState, useEffect } from 'react';
import produce from 'immer';
import dayjs from 'dayjs';
import { WithT } from 'i18next';
import * as ErrorHandler from 'components/ErrorHandler';
import useOnScreen from 'utils/useOnScreen';
import getLang from 'utils/getLang';
import Message from './Message';
const limit = 10;
const lang = getLang();
interface IMessagesProps extends WithT {
messages: any;
currentUserID: string;
chatID: string;
fetchMore: any;
typingText: any;
setSelectedMsg: any;
removeMessage: any;
}
const Messages: React.FC<IMessagesProps> = ({
messages,
currentUserID,
chatID,
fetchMore,
setSelectedMsg,
removeMessage,
t,
}) => {
const elementRef = useRef(null);
const isOnScreen = useOnScreen(elementRef);
const topElementRef = useRef(null);
const topIsOnScreen = useOnScreen(topElementRef);
const isUserInside = useRef(true);
const scroller = useRef<HTMLDivElement>(null);
const messagesEnd = useRef<HTMLDivElement>(null);
const [hasMore, setHasMore] = useState(true);
useEffect(() => {
scrollToBottom();
}, []);
useEffect(() => {
autoscroll();
}, [messages]);
//NOT WORKING
const autoscroll = () => {
// Visible height
const visibleHeight = scroller.current.offsetHeight;
// Height of messages container
const containerHeight = scroller.current.scrollHeight;
// How far have I scrolled?
const scrollOffset = scroller.current.scrollTop + visibleHeight;
// New message element
const firstChild = scroller.current.firstElementChild;
console.log(`visibleHeight`, visibleHeight);
console.log(`containerHeight`, containerHeight);
console.log(`scrollOffset`, scrollOffset);
console.log(`firstChild`, firstChild.offsetHeight);
console.log(`firstChild`, firstChild.scrollHeight);
console.log(`firstChild`, firstChild);
scroller.current.scrollTop = scrollOffset;
// // Height of the new message
// const newMessageStyles = getComputedStyle($newMessage)
// const newMessageMargin = parseInt(newMessageStyles.marginBottom)
// const newMessageHeight = $newMessage.offsetHeight + newMessageMargin
// // Visible height
// const visibleHeight = $messages.offsetHeight
// // Height of messages container
// const containerHeight = $messages.scrollHeight
// // How far have I scrolled?
// const scrollOffset = $messages.scrollTop + visibleHeight
// if (containerHeight - newMessageHeight <= scrollOffset) {
// $messages.scrollTop = $messages.scrollHeight
// }
};
const fetchDataForScrollUp = cursor => {
ErrorHandler.setBreadcrumb('fetch more messages');
if (!hasMore) {
return;
}
fetchMore({
variables: {
chatID,
limit,
cursor,
},
updateQuery: (previousResult, { fetchMoreResult }) => {
if (!fetchMoreResult?.getMessages || fetchMoreResult.getMessages.messages.length < limit) {
setHasMore(false);
return previousResult;
}
const newData = produce(previousResult, draftState => {
draftState.getMessages.messages = [...previousResult.getMessages.messages, ...fetchMoreResult.getMessages.messages];
});
return newData;
},
});
};
if (messages?.length >= limit) {
if (topIsOnScreen) {
fetchDataForScrollUp(messages[messages.length - 1].id);
}
}
if (isOnScreen) {
isUserInside.current = true;
} else {
isUserInside.current = false;
}
const scrollToBottom = () => {
if (messagesEnd.current) {
messagesEnd.current.scrollIntoView({ behavior: 'smooth' });
}
};
const groupBy = function (arr, criteria) {
return arr.reduce(function (obj, item) {
// Check if the criteria is a function to run on the item or a property of it
const key = typeof criteria === 'function' ? criteria(item) : item[criteria];
// If the key doesn't exist yet, create it
if (!Object.prototype.hasOwnProperty.call(obj, key)) {
obj[key] = [];
}
// Push the value to the object
obj[key].push(item);
// Return the object to the next item in the loop
return obj;
}, {});
};
const objectMap = object => {
return Object.keys(object).reduce(function (result, key) {
result.push({ date: key, messages: object[key] });
return result;
}, []);
};
const group = groupBy(messages, datum => dayjs(datum.createdAt).locale(lang).format('dddd, MMMM D, YYYY').toLocaleUpperCase());
const messageElements = objectMap(group)
.reverse()
.map((item, index) => {
const messageElements = item.messages
.map(message => {
return (
<Message
key={uniqueKey}
message={message}
currentUserID={currentUserID}
lang={lang}
removeMessage={removeMessage}
t={t}
chatID={chatID}
setSelectedMsg={setSelectedMsg}
/>
);
})
.reverse();
return messageElements;
})
.reduce((a, b) => a.concat(b), []);
return (
<div style={{ marginBottom: '25px' }}>
<div ref={topElementRef} />
<div
style={{
position: 'relative',
display: 'flex',
flexDirection: 'column',
flexWrap: 'wrap',
height: '100%',
overflow: 'hidden',
}}
ref={scroller}
>
{messageElements}
<div ref={elementRef} style={{ position: 'absolute', bottom: '5%' }} />
</div>
</div>
);
};
export default Messages;
Been stuck on this for 2 weeks lol. Any advice is helpful :)
Have you tried scrollIntoView ? you can try after changing your autoscroll function like following
const autoscroll = () => {
elementRef.current.scrollIntoView({ behavior: 'smooth' })
};
import React, {
useState
} from "react";
import GoogleMapReact from "google-map-react";
function GMap() {
const [latLgn, setLatLgn] = useState([{
lng: 24.7536,
lat: 59.437
},
{
lng: 24.7303,
lat: 59.4393
},
{
lng: 24.7387,
lat: 59.4497
},
]);
const [tallinn] = useState({
center: { // where i want to be centerd
lng: 24.7536,
lat: 59.437,
},
zoom: 10,
});
// Fit map to its bounds after the api is loaded
const apiIsLoaded = (map, maps, latlgn) => {
// Get bounds by our latlgn
const bounds = getMapBounds(map, maps, latlgn);
// Fit map to bounds
map.fitBounds(bounds);
// Bind the resize listener
bindResizeListener(map, maps, bounds);
};
// Re-center map when resizing the window
const bindResizeListener = (map, maps, bounds) => {
maps.event.addDomListenerOnce(map, "idle", () => {
maps.event.addDomListener(window, "resize", () => {
map.fitBounds(bounds);
});
});
};
// Return map bounds based on list of places
const getMapBounds = (map, maps, pins) => {
const bounds = new maps.LatLngBounds();
pins.forEach((pin) => {
bounds.extend(new maps.LatLng(pin[1], pin[0]));
});
return bounds;
};
return ( <
div >
<
div style = {
{
height: "100vh",
width: "100%"
}
} >
<
GoogleMapReact bootstrapURLKeys = {
{
key: AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk
}
}
defaultCenter = {
tallinn.center
}
defaultZoom = {
tallinn.zoom
}
onGoogleApiLoaded = {
({
map,
maps
}) => apiIsLoaded(map, maps, latLgn)
}
yesIWantToUseGoogleMapApiInternals >
{
latLgn.map((item, index) => ( <
div lat = {
item[1]
}
lng = {
item[0]
}
key = {
index
} >
</div>
))
} </GoogleMapReact>
</div>
</div>
);
}
export default Gmap
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
I have a problem with centralizing the map I am rendering.
although I have a state with the "lat" and "lng" as stated in the docs still, when I run the app with npm start or refreshing the page, it will centre itself somewhere in the ocean.\
PS. I will paste my "map" component only.
import React, { useState } from "react";
import GoogleMapReact from "google-map-react";
export default function GMap() {
const [latLgn, setLatLgn] = useState([{
lng: 24.7536,
lat: 59.437
},
{
lng: 24.7303,
lat: 59.4393
},
{
lng: 24.7387,
lat: 59.4497
},
]);
const [tallinn] = useState({
center: { // where i want to be centerd
lng: 24.7536,
lat: 59.437,
},
zoom: 10,
});
// Fit map to its bounds after the api is loaded
const apiIsLoaded = (map, maps, latlgn) => {
// Get bounds by our latlgn
const bounds = getMapBounds(map, maps, latlgn);
// Fit map to bounds
map.fitBounds(bounds);
// Bind the resize listener
bindResizeListener(map, maps, bounds);
};
// Re-center map when resizing the window
const bindResizeListener = (map, maps, bounds) => {
maps.event.addDomListenerOnce(map, "idle", () => {
maps.event.addDomListener(window, "resize", () => {
map.fitBounds(bounds);
});
});
};
// Return map bounds based on list of places
const getMapBounds = (map, maps, pins) => {
const bounds = new maps.LatLngBounds();
pins.forEach((pin) => {
bounds.extend(new maps.LatLng(pin[1], pin[0]));
});
return bounds;
};
return (
<div>
<div style={{ height: "100vh", width: "100%" }}>
<GoogleMapReact
bootstrapURLKeys={{key: AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk }}
defaultCenter={tallinn.center}
defaultZoom={tallinn.zoom}
onGoogleApiLoaded={({ map, maps }) => apiIsLoaded(map, maps, latLgn)}
yesIWantToUseGoogleMapApiInternals
>
{latLgn.map((item, index) => (
<div lat={item[1]} lng={item[0]} key={index}< </div>
))}
</GoogleMapReact>
</div>
</div>
);
}
ps. let me know if I should provide more information
UPDATE
I have created a sample project here.
I checked your code and I noticed a couple of things.
First, in your getMapBounds function, the bounds returned are null. This is because the values of pin[1] and pin[0] are undefined. This might be the reasoon why the map is centering in the middle of the ocean like this as this is the center of the world and coordinates are 0,0. You must use pin.lat and pin.lng instead so that it will correctly populate the value for bounds.
Second, it seems that you would like to put markers on the coordinates of your latLgn. To achieve this, you can follow the AnyReactComponent function as mentioned in the google-map-react docs instead of putting a div directly inside the latLgn.map.
Lastly, inside your latLgn.map, you must not use item[1] and item[0] as they are also both undefined instead use item.lat and item.lng.
Here's the code snippet for my working code:
import React, { useState } from "react";
import "./style.css"
import GoogleMapReact from "google-map-react";
const AnyReactComponent = ({ text }) => <div>{text}</div>;
export default function GMap() {
const [latLgn, setLatLgn] = useState([{
lng: 24.7536,
lat: 59.437
},
{
lng: 24.7303,
lat: 59.4393
},
{
lng: 24.7387,
lat: 59.4497
},
]);
const [tallinn] = useState({
center: { // where i want to be centerd
lng: 24.7536,
lat: 59.437,
},
zoom: 10,
});
// Fit map to its bounds after the api is loaded
const apiIsLoaded = (map, maps, latlgn) => {
// Get bounds by our latlgn
const bounds = getMapBounds(map, maps, latlgn);
// Fit map to bounds
map.fitBounds(bounds);
// Bind the resize listener
bindResizeListener(map, maps, bounds);
};
// Re-center map when resizing the window
const bindResizeListener = (map, maps, bounds) => {
maps.event.addDomListenerOnce(map, "idle", () => {
maps.event.addDomListener(window, "resize", () => {
map.fitBounds(bounds);
});
});
};
// Return map bounds based on list of places
const getMapBounds = (map, maps, pins) => {
const bounds = new maps.LatLngBounds();
pins.forEach((pin) => {
bounds.extend(new maps.LatLng(pin.lat, pin.lng));
console.log(pin[0])
});
return bounds;
};
return (
<div>
<div style={{ height: "100vh", width: "100%" }}>
<GoogleMapReact
bootstrapURLKeys={{key: "YOUR_API_KEY" }}
defaultCenter={tallinn.center}
defaultZoom={tallinn.zoom}
onGoogleApiLoaded={({ map, maps }) => apiIsLoaded(map, maps, latLgn)}
yesIWantToUseGoogleMapApiInternals
>
{latLgn.map((item, index) => (
<AnyReactComponent lat={item.lat} lng={item.lng} key={index} text={"(" +item.lat + "," + item.lng +")"}> </AnyReactComponent>
))}
</GoogleMapReact>
</div>
</div>
);
}
I also noticed your question in the comment section. You can use online ides like stackblitz to provide an sscce of your code and just remove the API key. You can put a note in your question that you need to put API key to see how the code works. You can refer to my working code above.
The project concept is to get geolocation and showing Mapbox map using API...
import ReactDOM from "react-dom";
import React, { useRef, useEffect } from "react";
import { geolocated } from "react-geolocated";
import mapboxgl from "mapbox-gl";
import fetchFakeData from "./api/fetchFakeData";
import Popup from "./components/Popup";
import "./App.css";
mapboxgl.accessToken ="pk.eyJ1IjoiamFja3Nvbi1rYXNpIiwiYSI6ImNrbzdsaDJvNTFvc3Eycm9pdTRxYmRxZjUifQ.BzA0w0U7lP0Ka3FcKkI_1Q";
const App = (props) => {
const mapContainerRef = useRef(null);
const popUpRef = useRef(new mapboxgl.Popup({ offset: 15 }));
// initialize map when component mounts
useEffect(() => {
const map = new mapboxgl.Map({
container: mapContainerRef.current,
// See style options here: https://docs.mapbox.com/api/maps/#styles
style: "mapbox://styles/mapbox/streets-v11",
center: [props.lat, props.long],
zoom: 12.5
});
// add navigation control (zoom buttons)
map.addControl(new mapboxgl.NavigationControl(), "bottom-right");
map.on("load", () => {
// add the data source for new a feature collection with no features
map.addSource("random-points-data", {
type: "geojson",
data: {
type: "FeatureCollection",
features: []
}
});
// now add the layer, and reference the data source above by name
map.addLayer({
id: "random-points-layer",
source: "random-points-data",
type: "symbol",
layout: {
// full list of icons here: https://labs.mapbox.com/maki-icons
"icon-image": "bakery-15", // this will put little croissants on our map
"icon-padding": 0,
"icon-allow-overlap": true
}
});
});
map.on("moveend", async () => {
// get new center coordinates
const { lng, lat } = map.getCenter();
// fetch new data
const results = await fetchFakeData({ longitude: lng, latitude: lat });
// update "random-points-data" source with new data
// all layers that consume the "random-points-data" data source will be updated automatically
map.getSource("random-points-data").setData(results);
});
// change cursor to pointer when user hovers over a clickable feature
map.on("mouseenter", "random-points-layer", (e) => {
if (e.features.length) {
map.getCanvas().style.cursor = "pointer";
}
});
// reset cursor to default when user is no longer hovering over a clickable feature
map.on("mouseleave", "random-points-layer", () => {
map.getCanvas().style.cursor = "";
});
// add popup when user clicks a point
map.on("click", "random-points-layer", (e) => {
if (e.features.length) {
const feature = e.features[0];
// create popup node
const popupNode = document.createElement("div");
ReactDOM.render(<Popup feature={feature} />, popupNode);
// set popup on map
popUpRef.current
.setLngLat(feature.geometry.coordinates)
.setDOMContent(popupNode)
.addTo(map);
}
});
// clean up on unmount
return () => map.remove();
}, []); // eslint-disable-line react-hooks/exhaustive-deps
return <div className="map-container" ref={mapContainerRef} />
};
export default geolocated({
positionOptions: {
enableHighAccuracy: false
},
userDecisionTimeout: 5000
})(App);
class Gps extends React.Component {
constructor() {
super();
this.state = {
latitude: "",
longitude: ""
};
this.getMyLocation = this.getMyLocation.bind(this);
}
componentDidMount() {
this.getMyLocation();
}
getMyLocation() {
const location = window.navigator && window.navigator.geolocation;
if (location) {
location.getCurrentPosition(
(position) => {
this.setState({
latitude: position.coords.latitude,
longitude: position.coords.longitude
});
},
(error) => {
this.setState({
latitude: "err-latitude",
longitude: "err-longitude"
});
}
);
}}
render() {
const { latitude, longitude } = this.state;
return (
<div>
<App lat={latitude} long={longitude} />
</div>
);
}
}
please see this link "codesandbox" : https://codesandbox.io/s/determined-river-7dt0h?file=/src/App.js
props didn't work.
see this lines:
center: [props.lat, props.long]
<App lat={latitude} long={longitude} / >
screenshot
The Problem
The Lat and Lng were initialized to an empty string, but the center property is expecting an array of numbers.
Solution
In Gps component, initialize the lat and long state values to a number as opposed to an empty string
this.state = {
latitude: 38.8951,
longitude: -77.0364
};
In my case, I was using it for web and the issue was causing because of the height of the div in which I was rendering map component. When I set the minHeight for that div, it worked fine for me.