#react-google-maps/api - Loading map is blocking the main thread - reactjs

I am currently working on a website, where I use google maps to display some markers with places location.
I am using this npm package: https://www.npmjs.com/package/#react-google-maps/api.
The issue is that when I do site audit with Google Insights (https://developers.google.com/speed/pagespeed/insights/) I am getting very low mobile performnace. It is about 50.
This is considered a issue, because I am using Next.js (version 10.0.6). Audit score on desktop is great.
I think, that the main issue is with loading a map. This proccess is blocking my website for about 500 ms. It is a lot.
Screen shot of an issue
Here is how I am getting the map at the moment:
I am using functional component
I am importing GoogleMap and useLoadScript from #react-google-maps/api
Loading the map
Rendering the map (onLoad is used to set ref)
I have already tried usingEffect (with an empty array), async/await syntax, nothing helped.
I will be very grateful for any help.
Kind regards,
Bartek

Although I am not that familiar on how Google Insight works, loading the script directly instead of relying on other 3rd party libraries (#react-google-maps/api) could prove beneficial and should reduce the latency.
App.js
import React, { Component } from 'react';
import { render } from 'react-dom';
import Map from './components/map';
import "./style.css";
class App extends Component {
render() {
return (
<Map
id="myMap"
options={{
center: { lat: -33.8569, lng: 151.2152 },
zoom: 8
}}
/>
);
}
}
export default App;
map.js
import React, { Component } from "react";
import { render } from "react-dom";
class Map extends Component {
constructor(props) {
super(props);
this.state = {
map: ""
};
}
onScriptLoad() {
this.state.map = new window.google.maps.Map(
document.getElementById(this.props.id),
this.props.options
);
var marker = new window.google.maps.Marker({
position: { lat: -33.8569, lng: 151.2152 },
map: this.state.map,
title: "Hello Sydney!"
});
//adding markers by click
this.state.map.addListener("click", e => {
//Determine the location where the user has clicked.
this.addMarker(e.latLng);
});
}
componentDidMount() {
if (!window.google) {
var s = document.createElement("script");
s.type = "text/javascript";
s.src = `https://maps.google.com/maps/api/js?key=YOUR_API_KEY`;
var x = document.getElementsByTagName("script")[0];
x.parentNode.insertBefore(s, x);
s.addEventListener("load", e => {
this.onScriptLoad();
});
} else {
this.onScriptLoad();
}
}
addMarker(latLng) {
var marker = new google.maps.Marker({
position: latLng,
map: this.state.map
});
}
render() {
return <div className="map" id={this.props.id} />;
}
}
export default Map;

Related

marker is showing out of the map in mapboxgl react project

I am using mapboxgl for the first time. i found the source code from github. the map is working fine but the marker is showing outside of the map. don't understand what is the problem..
code is given below_
import React, { Component } from "react";
import ReactDom from "react-dom";
import mapboxgl from "mapbox-gl";
mapboxgl.accessToken =
"pk.eyJ1Ijoibml0dGFuYW5kbyIsImEiOiJja3AzeGsxdXUxd2dtMnBvMjFncHV2MWQzIn0.T3wHeHz_mCHk1AcExPm8mA";
const data = [
{
location: "Panthapath",
city: "Dhaka",
state: "Bangladesh",
coordinates: [90.38382231219224, 23.75296142856617],
},
];
class Gmap extends React.Component {
constructor(props) {
super(props);
this.state = {
lng: 90.38382231219224,
lat: 23.75296142856617,
zoom: 16,
};
}
componentDidMount() {
const map = new mapboxgl.Map({
container: this.mapContainer,
style: "mapbox://styles/mapbox/streets-v11",
center: [this.state.lng, this.state.lat],
zoom: this.state.zoom,
});
data.forEach((location) => {
console.log(location);
var marker = new mapboxgl.Marker()
.setLngLat(location.coordinates)
.setPopup(
new mapboxgl.Popup({ offset: 30 }).setHTML(
"<h4>" + location.city + "</h4>" + location.location
)
)
.addTo(map);
});
}
render() {
return (
<div>
<div
ref={(el) => (this.mapContainer = el)}
style={{ width: "80%", height: "80vh", margin: "0 auto" }}
/>
</div>
);
}
}
export default Gmap;
in the terminal it is showing a error "Marker is defined but never used". But the map is loading as expected with the marker out of the map section
you can import mapbox-gl.css file in App.js. This is valid also for nextjs. In next.js you can add it in pages/next.js if your Map component in that.
import 'mapbox-gl/dist/mapbox-gl.css';
I found a solution.
after add mapbox-gl.css in index.html file marker was ok.
<link href="https://api.mapbox.com/mapbox-gl-js/v2.6.1/mapbox-gl.css" rel="stylesheet">
my program is react.

Error adding Google Maps with react - TypeError: Cannot read property 'maps' of undefined

// App.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
import Map from "./Map.js"
class App extends React.Component {
constructor(props) {
super(props);
this.loadScript = this.loadScript.bind(this);
}
loadScript() {
const API_KEY = process.env.REACT_APP_API_KEY;
const url = `https://maps.googleapis.com/maps/api/js?key=${API_KEY}&libraries=places`;
const s = document.createElement("script");
s.src = url;
document.head.appendChild(s);
}
componentWillMount() {
this.loadScript();
}
render() {
return (
<div>
<Map />
</div>
);
}
}
export default App;
//Map.js
import React from "react"
export default class Map extends React.Component {
constructor(props) {
super(props);
this.loadMap = this.loadMap.bind(this);
}
loadMap() {
const map = new window.google.maps.Map(document.getElementById('map'), {
center: { lat: -34.397, lng: 150.644 },
zoom: 8
});
}
componentWillMount() {
this.loadMap();
}
render() {
return (
<div id="map" style={{ width: 100, height: 100 }}></div>
);
}
}
Hi there, I am totally new to React, I am trying to load Google Maps without the help of a third-party library.
I have dynamically created a script tag and inserted it into the DOM. I am getting this error, trying to access the variables in the script.
My guess is that 'maps' is being accessed before the script is loaded. I am lost, on how to fix this error.
When you load a script programmatically you can listen for "onload" event and do the rest of your logic when the script will be loaded. In this case, your loadScript function might look like this:
loadScript() {
const API_KEY = process.env.REACT_APP_API_KEY;
const url = `https://maps.googleapis.com/maps/api/js?key=${API_KEY}&libraries=places`;
const s = document.createElement("script");
s.src = url;
document.head.appendChild(s);
s.onload = function(e){
console.info('googleapis was loaded');
}
}
You can add a scriptLoaded state to your App component and change it in the onload function, in this case, you need to render only if scriptLoaded is true :
<div>
{this.state.scriptLoaded && <Map />}
</div>

How to use leaflet-polylinedecorator in react 16.4.1

I'm trying to use the leaflet plugin polylinedecorator in react 16.4.1 (so without hooks). However, the only example I have been able to find on how to use this plugin in react is using hooks (see: How to use polylinedacorator with react leaflet), and I am unsure how I can adapt this to be able to use it in my code.
What I have so far is this polylinedecorator component:
import React, { Component } from "react";
import { Polyline } from "react-leaflet";
import L from "leaflet";
import "leaflet-polylinedecorator";
export default class PolylineDecorator extends Component {
componentDidUpdate() {
if (this.props.map) {
const polyline = L.polyline(this.props.positions).addTo(this.props.map);
L.polylineDecorator(polyline, {
patterns: [
{
offset: "100%",
repeat: 0,
symbol: L.Symbol.arrowHead({
pixelSize: 15,
polygon: false,
pathOptions: { stroke: true }
})
}
]
}).addTo(this.props.map);
}
}
render() {
return <></>;
}
}
That I am using like this:
import React, { Component } from "react";
import { Polyline, LayerGroup } from "react-leaflet";
import PolylineDecorator from "./PolylineDecorator";
export default class RouteLayer extends Component {
render() {
const { beginLocations } = this.props;
const locations = [];
const differentLocations: [];
beginLocations.forEach((location, index) => {
// some filtering going on here and pushing the locations to one of the
two arrays (locations, differentLocations)
});
return (
<LayerGroup>
<PolylineDecorator
map={this.props.map}
positions={locations}
color="#4e5c8d"
interactive={false}
/>
<PolylineDecorator
map={this.props.map}
positions={differentFloorLinesLocations}
color="red"
interactive={false}
/>
</LayerGroup>
);
}
}
The RouteLayer is nested inside the map as follows (for simplicity some components are left out):
<LeafletMap
ref={r => {
this.map = r;
if (this.props.setRefMap) {
this.props.setRefMap(r);
}
}}>
<RouteLayer
map={this.map ? this.map.leafletElement : null}
locations={locations}
/>
</LeafletMap>
Right now the polylines are drawn, however not quite as expected since the filtering doesn't seem to work (this filtering worked fine when I was just using polylines without the decorator).
The arrows I am trying to decorate the lines with are showing up, so that's good. However, I'm not happy with how the PolylineDecorator class is looking right now, this doesn't seem like the correct way to do this.
I'm also unsure if it is correct to pass the reference to the map in the way that I'm doing here.
Any suggestions on how to make this work correctly are appreciated.
For React version < 16.8 the following component demonstrates how to integrate L.polylineDecorator with React-Leaflet:
import React, { Component } from "react";
import { Polyline, withLeaflet } from "react-leaflet";
import L from "leaflet";
import "leaflet-polylinedecorator";
class PolylineDecorator extends Component {
constructor(props) {
super(props);
this.polyRef = React.createRef();
}
componentDidMount() {
const polyline = this.polyRef.current.leafletElement; //get native Leaflet polyline
const { map } = this.polyRef.current.props.leaflet; //get native Leaflet map
L.polylineDecorator(polyline, {
patterns: this.props.patterns
}).addTo(map);
}
render() {
return <Polyline ref={this.polyRef} {...this.props} />;
}
}
export default withLeaflet(PolylineDecorator);
Usage
export default class MyMap extends Component {
render() {
const { center, zoom } = this.props;
const polyline = [[57, -19], [60, -12]];
const arrow = [
{
offset: "100%",
repeat: 0,
symbol: L.Symbol.arrowHead({
pixelSize: 15,
polygon: false,
pathOptions: { stroke: true }
})
}
];
return (
<Map center={center} zoom={zoom}>
<TileLayer url="http://{s}.tile.osm.org/{z}/{x}/{y}.png" />
<PolylineDecorator patterns={arrow} positions={polyline} />
</Map>
);
}
}
Here is a demo

Load Google Maps in React App - 'map is assigned a value but never used'

I am trying to render a map on a page using React but the map never displays. In console I can see this warning and it seems that the variable is not called anywhere. I have seen common implementations like this work on tutorials but I dont understand why it's not rendering here.
This is my App component code:
import React, {Component} from 'react';
import './App.css';
class App extends Component {
componentDidMount() {
this.renderMap();
}
renderMap = () => {
this.loadScript("https://maps.googleapis.com/maps/api/" +
"js?key=AIzaSyC1FpwqY0kv0hxQYPtGnn54ag14jHUI6Ow&callback=initMap");
window.initMap = this.initMap;
};
loadScript = (url) => {
const index = window.document.getElementsByTagName("script")[0];
const script = window.document.createElement("script");
script.src = url;
script.async = true;
script.defer = true;
index.parentNode.insertBefore(script, index);
};
initMap = () => {
var map = new window.google.maps.Map(document.getElementById('map'), {
center: {lat: -34.397, lng: 150.644},
zoom: 8
});
};
render() {
return (
<div className="App">
<h1>Google Maps in React App</h1>
<div id="map"> </div>
</div>
);
}
}
export default App;
This is the warning in console:
webpackHotDevClient.js:120 ./src/App.js
Line 26: 'map' is assigned a value but never used no-unused-vars
I have loaded the script in the component dynamically. Can someone help me out? Thanks.
That just means that you defined the variable map, but don’t use it. This warning comes from eslint and not google maps. In initMap, you may just add return map
Additionnally, you can also disable this eslint rule in your eslint config as explained here

Using Phaser together with React

I am trying to understand how I could use React for rendering all the UI elements and Phaser for rendering a game (using Canvas or WebGL) within the React application.
One way would be to use React's componentDidMount, so after I have my HTML ready I can use a div id to render the Phaser content. In this case who does the rendering? React of Phaser?
If the mentioned way is not the right one, could you suggest another?
There is a module for this on Github called react-phaser. Here is a CodePen that does not use any separate module.
var PhaserContainer = React.createClass({
game: null,
...
componentDidMount: function(){
this.game = new Phaser.Game(800, 400, Phaser.AUTO, "phaser-container",
{
create: this.create,
update: this.update
});
},
...
render: function(){
return (
<div className="phaserContainer" id="phaser-container">
</div>
);
}
...
}
This is not my CodePen. Credit goes to Matthias.R
Check this new WebComponent to integrate Phaser with any other framework 👍 https://github.com/proyecto26/ion-phaser
Example:
import React, { Component } from 'react'
import Phaser from 'phaser'
import { IonPhaser } from '#ion-phaser/react'
class App extends Component {
state = {
initialize: false,
game: {
width: "100%",
height: "100%",
type: Phaser.AUTO,
scene: {}
}
}
render() {
const { initialize, game } = this.state
return (
<IonPhaser game={game} initialize={initialize} />
)
}
}

Resources