Markers not working with google-map-react - reactjs

I'm working with the 'google-map-react' library and I have tried all but the markers are not showing up.
I pass the coords to the marker in many ways but none worked.
Here's my code & repository:
https://github.com/jorginyu/ubica
import React, { Component } from 'react';
import GoogleMapReact from 'google-map-react';
const API_KEY = 'WTFULOOKINAT';
const contacts = [
{ name: 'Spiderman', lat: 41.529616, lng: 2.434130 },
{ name: 'Iron Man', lat: 41.528103, lng: 2.433834 },
{ name: 'Hulk', lat: 41.530192, lng: 2.422994 }
];
const MarkersC = (text ) => <div className="contact">{text}</div>;
export default class MapComponent extends Component {
constructor(props) {
super(props);
this.state = {
center: {
lat: 41.528452,
lng: 2.434195
},
zoom: 18
}
}
render() {
return (
// Important! Always set the container height explicitly
<div className="mt-5" style={{ height: '80vh', width: '100%' }}>
<GoogleMapReact
bootstrapURLKeys={{ key: API_KEY }}
defaultCenter={this.state.center}
defaultZoom={this.state.zoom}
>
{contacts.map((contact,i) => {
<MarkersC position={{lat: contact.lat, lng: contact.lng}} text={contact.name} key={i} />
})}
</GoogleMapReact>
</div>
);
}
}
What can I do? Thanks for your time :)

There is a formatting problem. I deleted the spaces:
THEN:
{
contacts.map((contact, i) =>
<MarkersC lat={contact.lat} lng={contact.lng} text={contact.name} key={i} />
)
}
NOW:
{
contacts.map((contact, i) => <MarkersC lat={contact.lat} lng={contact.lng} text={contact.name} key={i} /> )
}

If you open the browser's console, you will see an error. The problem is with your MarkerC component and how you try to get the text prop.
The parameter of the component is an object with all properties that are passed to it.
You do not destructure it to get the text you simply use the whole parameter and try to display it.
So you need to propertly destructure it as const MarkersC = ( {text} ) => ..
Instead of
const MarkersC = ( text ) => <div className="contact">{text}</div>;
it should be
const MarkersC = ( {text} ) => <div className="contact">{text}</div>;
Update
Just noticed, the google-map-react expect to find lat and lng properties on the marker. You have wrapped them inside a position property so they cannot be found.
So your usage should be
either
<MarkersC lat={contact.lat} lng={contact.lng} text={contact.name} key={i} />
or spread the whole contact object that holds those properties
<MarkersC {...contact} key={i} />
so that the lat,lng and text are all direct properties of the MarkersC component.

Declaring the object type and referencing directly the value of 'text' on the initial 'const' worked for me:
const AnyReactComponent = (text:any) => <div>{text.text}</div>;

In your question, you have provided invalid GoogleMap key so it is showing error in console Google Maps JavaScript API error: InvalidKeyMapError.
So provide a valid GoogleMap key or blank(const API_KEY = '') for development use only.
In my case with blank API_KEY, it's working fine.
(Your git repo code is different than code you have posted here in StackOverflow.)

Related

Rendering a MapContainer on several subpages results in error

I'm having some issues with MapContainer from react-leaflet throwing errors at me in my app.
I have a tabel of events, that links to a page with event details, this details page implements a map with the following component:
export function MapWithPoints(props: Props) {
const { points } = props
return (
<MapContainer scrollWheelZoom={false} style={{ height: 300, width: '100vw' }}>
<InnerMap points={points} />
</MapContainer>
)
}
InnerMap is defined as:
function InnerMap(props: Props) {
const { points } = props
const leafletMap = useMap()
React.useEffect(() => {
if (leafletMap && points.length) {
const pointArray: L.LatLngTuple[] = points.map((item) => [
item.lat,
item.lng,
])
const bounds = L.latLngBounds(pointArray)
leafletMap.fitBounds(bounds)
}
}, [points, leafletMap])
return (
<React.Fragment>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{points
.filter((poi) => poi.lat && poi.lng)
.map((poi) => (
<Marker
position={[poi.lat, poi.lng]}
key={`${poi.id}_${poi.lat}_${poi.lng}`}
icon={poi.icon ? poi.icon : DefaultIcon}
>
{poi.description ? <Popup>{poi.description}</Popup> : null}
</Marker>
))}
</React.Fragment>
)
}
The map works when I go to the first (random) entry in my list of events, the event page loads like it should, but if I go back and then choose another event page, MapContainer throws an error:
Uncaught Error: Map container is already initialized.
at NewClass._initContainer (Map.js:1092:1)
at NewClass.initialize (Map.js:136:1)
at new NewClass (Class.js:24:1)
at MapContainer.js:31:1
at commitAttachRef (react-dom.development.js:23645:1)
at safelyAttachRef (react-dom.development.js:22891:1)
at reappearLayoutEffectsOnFiber (react-dom.development.js:23545:1)
at reappearLayoutEffects_complete (react-dom.development.js:24838:1)
at reappearLayoutEffects_begin (react-dom.development.js:24826:1)
at commitLayoutEffects_begin (react-dom.development.js:24649:1)
I'm using react 18, and react-leaflet 4.0.0
Is there something I have missed in the setup?
There is a bug in 4.0.0, but even when using the latest version, I still get the error now and then. A workaround is to have a unique key on Map Container, for example timestamp. But this result in a memory leak, as each time a map is loaded it consumes more memory

react-leaflet typeError when trying to render coordinates

I'm working on a React project with Redux / thunk and I have this map from react-leaflet :
import { MapContainer, Marker, Popup, TileLayer, Circle } from "react-leaflet";
import { useSelector } from "react-redux";
function Maps() {
const user = useSelector((state: any) => state.userReducer.getUserById);
const coords: [number, number] = [user ? user.latitude : 0, user ? user.longitude : 0];
const fillBlueOptions = { fillColor: "blue" };
const zone = 10000;
return (
<MapContainer className="maps" center={coords} zoom={10} scrollWheelZoom={false}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Circle center={coords} pathOptions={fillBlueOptions} radius={zone} />
<Marker position={coords}>
<Popup>Adress</Popup>
</Marker>
</MapContainer>
);
}
export default Maps;
I get the coordinates of the user with an axios call :
export const getUserById = (id_user: number | string) => {
return (dispatch: any) => {
dispatch(isLoading(true));
return axios
.get(`users/getUserById/${id_user}`)
.then((res) => {
dispatch(isLoading(false));
dispatch({ type: GET_USER_BY_ID, payload: res.data });
})
.catch((err) => {
console.log(err);
dispatch(isLoading(false));
});
};
};
The dispatch is launched here when i click on this table :
<tbody>
{users.slice(0, 20).map((item: any, index: number) => (
<tr
className="redirectUser"
onClick={() => {
history.push("/Utilisateurs");
dispatch(getUserById(item.id));
}}
key={index}
>
<td>{item.id}</td>
<td>
{item.nom} {item.prenom}
<br></br>Tcheker
</td>
<td>{item.email}</td>
<td>18/10/1998</td>
<td>{item.tel}</td>
</tr>
))}
</tbody>
When I try to display the map component on a user who has the coords, i have this error (or the map just show blank screen) :
Uncaught TypeError: Cannot read property 'latitude' of undefined
Then if i go back in the history and re-click on the table, the map finally displays with no error.
When the user doesn't have any coords I get this error :
Uncaught TypeError: Cannot read property 'lng' of null
I understand that the component seems to render before getting the datas, but even with a ternary operator that gives a hard-coded value in case of an undefined answer, the component doesn't render neither.
I think this is an async issue. The very nature of your coords variable looks to be non-blocking and hence the render is performed before the ternary can be performed.
Since coords is just a variable (and not state driven) it never re-renders.
If you could set the state at the time coords is calculated and then refer you're leaflet map to the variables in state, I think you could get it working.
Good luck,
Phil

ReactJS sending ref to global useContext state (Konva)

I am using useContext as a global state solution. I have a Store.jsx which contains my state, and a reducer.jsx which reduces. I am using Konva to create some shapes on an HTML5 Canvas. My goal is when I click on a shape I want to update my global state with a reference to what is active, and when I click again, to clear the reference.
My Full Code can be found here:
https://codesandbox.io/s/staging-platform-2li83?file=/src/App.jsx
Problem:
The problem is when I update the global state via the onClick event of a shape, its says that the reference is 'null', but when I console.log the reference in the onClick I can see the correct reference.
I think I am missing an important point to how useRef works.
This is how the flow appears in my head when I think about this:
I create a canvas, and I map an array of rectangle properties. This creates 4 rectangles. I use a wrapper component that returns a rectangle.
{rectArray.map((rectangle, index) => {
return (
<RectWrapper key={index} rectangle={rectangle} index={index} />
);
})}
Inside the RectWrapper, I create a reference, pass it to the ref prop of the Rect. In the onclick function, when I console log 'shapeRef' I see the refence ONLY when dispatch is commented out. If I uncomment dispatch then it shows as null, and if I console log the state, the reference is always null.
const RectWrapper = ({ rectangle, index }) => {
const shapeRef = React.useRef();
return (
<Rect
x={rectangle.x + index * 100}
y={5}
width={50}
height={50}
fill="red"
ref={shapeRef}
onClick={() => {
console.log("ShapeRef: ");
console.log(shapeRef); // This correctly identifies the rect only when dispatch is uncommented
dispatch({
type: "active_image",
payload: {
index: index,
reference: shapeRef
}
});
}}
/>
);
};
perhaps I am going about this to wrong way with hooks. I am just trying to keep a global state of whats been clicked on because components in another file would rely on this state.
The problem is happening because you are creating RectWrapper component as a functional component within your App component causing a new reference of the component to be created again and again and thus the reference is lost
Move your RectWrapper into a separate component declared outside of App component and pass on dispatch as a prop to it
import React, { useEffect, useContext, useState, Component } from "react";
import { Stage, Layer, Rect, Transformer } from "react-konva";
import { Context } from "./Store.jsx";
import "./styles.css";
const RectWrapper = ({ rectangle, index, dispatch }) => {
const shapeRef = React.useRef();
return (
<Rect
x={rectangle.x + index * 100}
y={5}
width={50}
height={50}
fill="red"
ref={shapeRef}
onClick={() => {
console.log("ShapeRef: ");
console.log(shapeRef);
dispatch({
type: "active_image",
payload: {
index: index,
reference: shapeRef
}
});
}}
/>
);
};
export default function App() {
const [state, dispatch] = useContext(Context);
console.log("Global State:");
console.log(state);
const rectArray = [
{ x: 10, y: 10 },
{ x: 10, y: 10 },
{ x: 10, y: 10 },
{ x: 10, y: 10 }
];
return (
<div className="App">
<Stage height={500} width={500}>
<Layer>
{rectArray.map((rectangle, index) => {
return (
<RectWrapper
dispatch={dispatch}
key={index}
rectangle={rectangle}
index={index}
/>
);
})}
</Layer>
</Stage>
</div>
);
}
Working demo
I don't think you need to create a ref in RectWrapper, because onClick has one event parameter. And the ref of the element that was clicked can be found in the event:
onClick={(e) => {
const thisRef = e.target;
console.log(thisRef );
...
Here is a working version without useRef: https://codesandbox.io/s/peaceful-brook-je8qo

Why do I get Type Error when passing props in map?

I can't seem to pass the 'name' prop to the component without a type error. I have an array of objects being passed in as {reslist} and each object in that array has a logo, and name value. I cant seem to pass name or logo into my return.
import React from 'react';
import './Resources.css'
import CodeCard from '../CodeCard/CodeCard.js'
const Resources = ({ reslist }) => {
return (
<div>
<h3 className='resCall'>Check out my Code!</h3>
<div className='resCards'>
{ reslist.map((i) => {
return ( <CodeCard
key={i}
name={reslist[i].name}
/>
);
})
}
</div>
</div>
)
}
export default Resources;
Here is the error
TypeError: Cannot read property 'name' of undefined (anonymous
function)
Here is my Resource List
const ResList = [
{
logo: 'https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png',
name:'GitHub'
},
{
logo: 'https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png',
name:'GitHub'
},
{
logo: 'https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png',
name:'GitHub'
},
{
logo: 'https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png',
name:'GitHub'
},
]
export default ResList
Your map callback has incorrect parameters. The first parameter is value, 2nd is index (ref):
reslist.map((value, index) => {
return (<CodeCard
key={index}
name={reslist[index].name}
/>)
})
But of course, you can use value directly too:
reslist.map((value, index) => {
return (<CodeCard
key={index}
name={value.name}
/>)
})
I'm assuming reslist is an array of objects. When you call .map on an array of objects, the first parameter (in your example, i) is each object. The second parameter is the index. I think you're getting these two mixed up.
reslist.map((item, i) => (
<CodeCard
key={i}
name={item.name}
/>
))
Importing components don't need to have .js:
edit it with import { CodeCard } from '../CodeCard'
ps: please don't shot me

How to get a ref to the dom node of a google map's marker in react?

Using the google-maps-react npm package I can get a ref to the map's dom node like so:
loadMap() {
const maps = this.props.google.maps;
const mapRef = this.refs.map; <---- ref set in render function on dom node
const node = ReactDOM.findDOMNode(mapRef); <--- the dom node
...
this.map = new maps.Map(node, mapConfig);
this.mapRef = mapRef;
}
It's pretty easy because mapref is set in the render method:
render() {
return (
<div style={{ height: mapHeight }} ref="map">
...
And then that is used to set node and then that is used to new up the map.
How would I do this with a map's marker? A marker doesn't need a dom node to be created and therefore I cant get a ref to the marker.
this.marker = new google.maps.Marker({someOptions}); <----- no dom node needed
I want to do this because I need to dynamically change the icon of the marker based on some value in my redux store. I have tried changing the icon via props (see below), but it somehow prevents the icon marker from being draggable even though draggable is set to true.
return (
<Marker
key={foo}
position={latLngPos}
icon={ active ? activeIcon : defaultIcon }
draggable={true}
onDragstart={() => { return this.handleMapMarkerDragStart();}}
onDragend={() => { return this.handleMapMarkerDrop();}}
/>);
I suspect things are acting strangely because to get google's maps api to work with react, the components have to deal with the actual dom nodes instead of the virtual dom nodes.
Any insight into this would be much appreciated.
Regarding
I want to do this because I need to dynamically change the icon of the
marker based on some value in my redux store. I have tried changing
the icon via props (see below), but it somehow prevents the icon
marker from being draggable even though draggable is set to true.
the following example demonstrates how to:
set marker as draggable
set custom icon per marker
Example
const handleDragEnd = (props, marker, event) => {
console.log(event.latLng);
};
const defaultIcon =
"http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png";
const activeIcon =
"http://maps.google.com/mapfiles/kml/pushpin/pink-pushpin.png";
const MapWrapper = props => {
return (
<div className="map-container">
<Map
google={props.google}
className={"map"}
zoom={4}
initialCenter={{ lat: -24.9923319, lng: 135.2252427 }}
>
{props.places.map((place, i) => {
const active = i % 2 === 0;
return (
<Marker
icon={active ? activeIcon : defaultIcon}
key={place.id}
position={{ lat: place.lat, lng: place.lng }}
draggable={true}
onDragend={handleDragEnd}
/>
);
})}
</Map>
</div>
);
};
Note: Marker component does not support onDragstart event
listener at the moment in google-maps-react library, but it could be
attached directly to Google Maps Marker object
Demo

Resources