InfoWindow is showing two infowindows using react-google-maps - reactjs

I'm using the react-google-maps library and I've been doing pretty well so far. My app receives a json with locations from my backend and I'm mapping them to add Markers on the map, they are displaying just fine. These markers have an OnClick function which sets information on a ReactHook.
When my hook state changes, my InfoWindow triggers and displays itself and this is fine. The problem is a little InfoWindow with no information which triggers at the same time than the other, i have tried to find the error for a few hours now and I can't find it, I've checked the code and also I'be inspect the components from the Browser but that little InfoWindow is not being recognized by the react developer tools.
Here is my code:
function Map(props){
const [selectedTienda, setSelectedTienda] = useState(null)
const options ={
styles: MapStyles,
disableDefaultUI: true,
}
return(
<GoogleMap
defaultZoom={17}
defaultCenter={{lat: 29.0824139, lng: -110.966389}}
options={options}
>
{props.items.map(tienda =>(
<Marker
key={tienda['tienda.id']}
position={{
lat: parseFloat(tienda['tienda.ubicacion.lat']),
lng: parseFloat(tienda['tienda.ubicacion.lng'])
}}
onClick={()=>{
setSelectedTienda(tienda);
}}
/>
))}
{selectedTienda ? (
<InfoWindow position={{
lat: parseFloat(selectedTienda['tienda.ubicacion.lat']),
lng: parseFloat(selectedTienda['tienda.ubicacion.lng'])
}}
onCloseClick={()=>{setSelectedTienda(null)}}
>
<div><h4>This is the <br/>INFOWINDOW I WANT</h4></div>
</InfoWindow>
): null}
</GoogleMap>
)
}
const MyMapComponent = withScriptjs(withGoogleMap(Map))
Here is also an image of my nightmare:
Here is what my react dev tools is showing, which is only the main InfoWindow

So the thing that was giving me a headache was the React Strict Mode wrapper on the index.js, which renders my component twice. According to the React documentation,
StrictMode is a tool that helps highlight potential problems in application.
Like Fragment, StrictMode does not render any visible UI. It activates additional checks and warnings for its descendants.
StrictMode currently helps with:
Identifying components with unsafe lifecycles
Warning about legacy string ref API usage
Warning about deprecated findDOMNode usage
Detecting unexpected side effects
Detecting legacy context API
So in my case, the problem was that StrictMode was detecting unexpected side effects on my map component, which was intentionally double-invoking my useState function, making two InfoWindows appear, instead of just one.
My solution was to remove the React.StrictMode wrapper from my index.js file, and that was it, but I think there might be another solution to avoid those unexpected side effects.
More information on StrictMode and how it works:
https://reactjs.org/docs/strict-mode.html

I found a solution that works without removing the React.StricMode wrapper:
Just use InfoWindowF instead of InfoWindow (that applies to the html tags as well).

AirLancer's solution to use InfoWindowF worked for me too. Using MarkerF instead of Marker also helped solve some other bugs (not rendering immediately). According to another forum these are issues in React 18+

How to use InfoWindowF?
is this the way?
import {
InfoWindowF,
} from "#react-google-maps/api";
{selectedMarker ? (
<InfoWindowF
position={{ lat: selectedMarker.lat, lng: selectedMarker.lng }}
onCloseClick={(event) => handleInfoWindow(event)}
onOpen={() => {
console.log("InfoWindow opened!");
}}
onClose={() => {
console.log("InfoWindow closed!");
}}
children={<p>{infoData.Name}</p>}
></InfoWindowF>
) : null}

Related

Custom Marker as a React Component with react-google-maps-api

We are converting from google-map-react to react-google-maps-api which means more of a native Google Maps API experience.
Prior to the change, we were able to load a custom Marker as a React Component like:
<GoogleMaps>
<UserPinContainer lat={userLocation.lat} lng={userLocation.lng}>
<Tidal.Icon type="UserLocation" text="User" />
</UserPinContainer>
</GoogleMaps>
From their docs of our original lib google-map-react, it says:
Instead of the default Google Maps markers, balloons and other map components, you can render your cool animated react components on the map.
While that is great, it proves this library moves away from the native API...
However, with the new Marker API from react-google-maps-api, React Components are not possible. They only expect string | google.maps.Icon | google.maps.Symbol; types, such as:
<Marker
icon={{
path: google.maps.SymbolPath.CIRCLE,
scale: 7,
}}
position={centers[0]}
/>
or
<Marker position={{ lat: userLocation.lat, lng: userLocation.lng }}>
<Tidal.Icon img type="UserLocation" text="User" />
</Marker>
I thought I could create a true custom-marker like:
const newMarker = new window.google.maps.Marker(MyComponent)
but no such luck.
What would be a good solution for this?
Maybe it's a bit late to answer, but if it helps, you can use the OverlayView component instead of Marker, it works practically the same and you must pass a react component as children.
<GoogleMap
id="overlay-view-example"
mapContainerStyle={mapContainerStyle}
zoom={11}
center={center}
>
<OverlayView
position={center}
mapPaneName={OverlayView.OVERLAY_MOUSE_TARGET}
>
<div style={divStyle}>
<h1>OverlayView</h1>
<button
onClick={onClick}
type='button'
>
Click me
</button>
</div>
</OverlayView>
</GoogleMap>
This is an example taken from the doc:
https://react-google-maps-api-docs.netlify.app/#overlayview

React <video/> flickers and restarts on parent rerender

I am trying to use a video in a component, but whenever a parent is rerendered (unavoidable in this situation, due to a lot of complex functionality) the video tag flashes/flickers and restarts. I have tried using useMemo and seperating into a seperate component and implementing React.memo(), shouldComponentUpdate(), and extending PureComponent (although i recognize its not recommended to use these to reliably prevent a rerender in the docs). I am thinking it some problem with the functionality of the html video player or something right now because i tried logging on the components rerender and am seeing the behaviour without any log to the console. In this visual example you can see on the top the component with this behaviour, and on the bottom another component (That has almost the same code) with a gif that uses image instead of video
let url = "url here"
const renderVideo = useMemo(
() => (
<video
src={url}
controls
muted={true}
autoPlay
height="100%"
width="100%"
style={{
borderRadius: "0.25rem",
}}
/>
),
[url, keepScale, fit]
);
return (
<Widget
{...props}
editorContent={<EditorContent />}
>
{renderVideo}
</Widget>
);
that should be the relevent code. This component is pretty simple, complex functionality is implemented in the Widget component you can see

Fit a map's bounds to contents of a FeatureGroup in React-Leaflet

I have a FeatureGroup with a Circle inside of it. On load, I want the map's viewport to completely show the circle. There are a number of relevant StackOverflow questions (this one, another one, a third), and I have tried them all. None have worked for me using modern tools.
import { MapContainer, TileLayer, FeatureGroup, Circle } from 'react-leaflet'
export default function MyMap({volunteer, pollingPlaces}) {
const coordinates = [40.781753, -73.966583];
const circle = <Circle pathOptions={{color: 'purple'}} center={coordinates} radius={3*1609} />
return (
<MapContainer center={coordinates}
style={{
height:"400px",
width: "400px"
}}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<FeatureGroup>
{circle}
</FeatureGroup>
</MapContainer>
)
}
One of the big problems is that string refs are now deprecated, and most of the old answers rely on this. It's also not clear to me how to use the new useMap hook in concert with the createRef method in a function component. (The third approach above seems to get the closest, and is from just a year ago, but it still relies on componentDidMount.) Finally, I am unable to get my FeatureGroup to call either the onadd or onAdd method (it's unclear which is correct? see comment below the answer…) when I pass it to the component. The 2018 answer relies on this callback.
The combination of these three issues mean I can't get any of the old solutions working. So I thought I'd ask anew: what's the right way to do this these days?
For the question described in the title, as you have another one later regarding onAdd and it is better to ask it separately in my opinion, you can still use a ref to get a reference to the FeatureGroup
All you need is to call map.fitBounds. To get the map reference you need to use whenCreated prop inside the component that includes MapContainer. If you were inside a child you would have to use useMap hook to get it. Once you get this you need to use the featureGroup ref which is a react ref, an object actually, and can be accessed via current. Inside there you have some leaflet methods. The one you need is getBounds method to retrieve the bounds of your feature group.
const [map, setMap] = useState(null);
const featureGroupRef = useRef();
useEffect(() => {
if (!map) return;
map.fitBounds(featureGroupRef.current.getBounds());
}, [map]);
<MapContainer
center={coordinates}
zoom={6}
style={{
height: "400px",
width: "400px"
}}
whenCreated={setMap}
>
...
<FeatureGroup ref={featureGroupRef}>{circle}</FeatureGroup>
</MapContainer>
Demo

Can't get Popover to display in correct position in Dialog

I have a Dialog and have a ListItem that when you click on it goes into edit mode by showing a Popover. This was working in an older version of MUI using a Modal but since getting on the latest that didn't work and I'm trying to use a Popover. I tried to make a simple example on CodeSandox but that works. What happens is the Popover is always in the upper left of the page instead of the ListItem.
I have simplified my code to a simple Button and Popover in the Dialog and still have the same problem and have ran out of ideas on what to try next. The error I get in the console is
[Warning] Material-UI: the `anchorEl` prop provided to the component is invalid.
The anchor element should be part of the document layout.
Make sure the element is present in the document or that it's not display none.
When the item is clicked I do event.currentTarget just like in the examples and this is what the console.log looks like for it.
[Log] <button class="MuiButtonBase-root MuiButton-root MuiButton-text" tabindex="0" type="button"> (main.chunk.js, line 26437)
<span class="MuiButton-label">Click Me</span>
<span class="MuiTouchRipple-root">
<span class="MuiTouchRipple-ripple MuiTouchRipple-rippleVisible" style="width: 117.2006825918689px; height: 117.2006825918689px; top: -34.60034129593445px; left: -25.60034129593445px;">
<span class="MuiTouchRipple-child MuiTouchRipple-childLeaving"></span>
</span>
</span>
</button>
I even tried doing disablePortal in the Dialog which didn't fix it. I also tried using refs which fixed the anchorEl warning but still displays relative to the page and not the element. Any ideas?
For anyone that comes across this issue with Material UI, there are a couple of things that you can do.
One is to make sure that if you have multiple nested functional components, that your anchorEl / click handlers for the popover are defined within the specific functional component that holds the popover. If you have nested functional components and the parent component holds the state, it will rerender the children on every state change, which can reset the anchorEl reference.
Second - React.memo can prevent unnecessary rerenders on functional components (it only works if props don't change but should still reap performance benefits in child components).
I have nested elements this is how I solved this without doing anything too extra.
So my main functional component simply returned something like this
const filters = () => {
const [anchorEl, setAnchorEl] = useState(null)
const popoverOpen = Boolean(anchorEl)
const handleTogglePopover = (event: any) => {
event.preventDefault()
if (anchorEl === null) {
setAnchorEl(event.currentTarget)
} else {
setAnchorEl(null)
}
}
const SortActions = () => {
return (
<Box>
<MyCustomRadioButton/>
</Box>
)
}
const FilterButtons = () => {
return (
<Box>
<ButtonBase
onClick={handleTogglePopover}
aria-owns={popoverOpen ? 'my-popover-id-name' : undefined}
aria-haspopup={true}
>
{/* contents (this is a comment in html in react) */}
</ButtonBase>
<Popover
id={'my-popover-id-name'}
open={popoverOpen}
anchorEl={anchorEl}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left'
}}
transformOrigin={{
vertical: 'top',
horizontal: 'left'
}}
onClose={handleTogglePopover}
>
<SortActions/>
</Popover>
</Box>
)
}
return (
<Box>
{/* THIS LINE IS THE LINE I CHANGED TO GET IT TO WORK */}
<FilterButtons/>
</Box>
)
}
I changed that line to {FilterButtons()} instead. It looks like the component that is rendering the popover needs to exist within the functional component calling it.
However any nested components under do not need to be declared within the calling functional component.
From what I have gathered in looking at many peoples solutions is to use React.memo for this but this worked for me. Something about React re-rendering the popover losing the state when its called as a nested component rather than a function within the component causes the state to be lost? I assume it has to do with how JavaScript works in terms of encapsulation within a function.
I know this is an older question but I know people will still run by this question eventually.
It's also possible to get this error due to what it's saying - you might be trying to use an element that has display: none style as an achorEl for your component, which isn't supported as the underlying logic calculating the position of the anchor element needs it to be visible on screen.
Check if there is any display: none; style
May be anchorEl used in multiple nested functional components problem
Try to use memo concept to prevent component rerender

react-google-maps pass props on marker click

I am trying to integrate "react-google-maps" in my app to display a map. I am having the hardest time understanding their system for markers.
https://tomchentw.github.io/react-google-maps/#marker
I am able to display my map and get all of my markers displaying correctly. The next step is where I am having problems. I need to be able to click on each marker and know know what marker is being clicked on. I have commented the following code to show what props I am trying to show in my console for now. I think that I just need to pass another argument in the event but I have no idea how to do that. Truthfully right now I believe I am passing the event listener but I not even fully sure what I am looking at when I log that 'e' variable.
You can see what I have so far in my github. If you click on the marker you will see what I am logging.
https://joweber123.github.io/take-me-there/login/James
Please help, I can't find this information discussed anywhere else. Thank you so so so much
import React, { Component } from 'react';
import { withGoogleMap, GoogleMap, Marker, } from "react-google-maps";
import { compose, withProps } from "recompose";
class List extends Component {
//I eventually will use this information to set state and all of that, but for now I just don't understand how to pass information to this function from my marker on onClick
handleMarkerClick(e){
console.log(e);
}
render(){
const { compose, lifecycle} = require("recompose");
const {
withGoogleMap,
GoogleMap,
Marker,
} = require("react-google-maps");
const MapWithAMarker = compose(
withGoogleMap
)(props =>
<GoogleMap
defaultZoom={8}
defaultCenter={{ lat: Number(`${this.props.locations[Object.keys(this.props.locations)[Object.keys(this.props.locations).length-1]].location.lat}`), lng: Number(`${this.props.locations[Object.keys(this.props.locations)[Object.keys(this.props.locations).length-1]].location.lng}`) }}
locations={this.props.locations}
>
{
Object
.keys(this.props.locations)
.map(key =>
<Marker
key={key}
position={{ lat: Number(`${this.props.locations[key].location.lat}`), lng: Number(`${this.props.locations[key].location.lng}`) }}
locations={this.props.locations[key]}
//I want to be able to pass the information that is stored in my locations prop here, but I have no idea how to do that.
onClick={props.onMarkerClick}
/>
)
}
</GoogleMap>
);
return (
<div className = "location-list one-third column center border-main full-height">
<MapWithAMarker
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `400px` }} />}
mapElement={<div style={{ height: `100%` }} />}
locations={this.props.locations}
//I am not even really sure what I am doing here. What gets printed out in my console gives me some information but what I really want to be able to do is to pass the locations props of my specific marker and have access to that information
onMarkerClick={(e)=>this.handleMarkerClick(e)}
/>
</div>
);
}
}
export default List;
I was able to figure it out! I was just passing the event listener before so of course it didn't contain any of the information that I needed. I also didn't need to pass it its parent as just having it trigger its own event was enough. Thanks everyone for having a look at this.
onClick={()=> this.props.handleMarkerClick(this.props.locations[key])}
I guess I wasn't asking myself the right question but once I found these two answers I understood much better what I needed to do.
React - Passing props to child onClick
Pass props to parent component in React.js
Can you show what is contained in this.props.locations? Usually you want to map the locations and each location should have some kind of id field. Instead of mapping through the keys you should map through the array of locations. That way you can pass the id (or even the coordinates if they are in the location object itself, which I presume they do) when the onClick function is called.

Resources