How to use react-leaflet-pixi-overlay - javascript

Im trying to implement the minimal example of the docs of react-leaflet-pixi-overlay. You can find the code below:
import React from "react";
import PixiOverlay from "react-leaflet-pixi-overlay";
import { Map, TileLayer } from "react-leaflet";
import { renderToString } from "react-dom/server";
import ReactDOM from "react-dom";
const App = () => {
const markers = [
{
id: "randomStringOrNumber",
iconColor: "red",
position: [-37.814, 144.96332],
popup: renderToString(<div>All good!</div>),
onClick: () => alert("marker clicked"),
tooltip: "Hey!"
},
{
id: "2",
iconColor: "blue",
position: [-37.814, 144.96332],
popup: "Quack!",
popupOpen: true, // if popup has to be open by default
onClick: () => alert("marker clicked"),
tooltip: "Nice!"
}
];
return (
<Map
preferCanvas={true}
maxZoom={20}
minZoom={3}
center={[-37.814, 144.96332]}
// Other map props...
>
<TileLayer
url="https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png"
attribution='© OpenStreetMap contributors'
/>
<PixiOverlay markers={markers} />
</Map>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I have implemented it in the following sandbox, but doesnt work:
_reactLeaflet.useLeaflet is not a function
Cant find any example on the internet.

Made it work using following versions:
leaflet 1.3.4
react 16.14.0
react-dom 16.14.0
react-leaflet 2.8.0
react-leaflet-pixi-overlay 1.0.10
react-scripts 1.1.4
Check the sandbox working

Related

tailwindcss react class text-color not working

so i'm learning tailwindcss with react and I've been trying to apply different classes on the body using the useEffect hook.
I did a small configuration on my tailconfig.js
/** #type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{html,js}', './src/components/*.(js)'],
theme: {
extend: {},
colors: {
body: '#DCDCDC',
'selected-text': '#A3A3FF',
theme: '#3F3FFF',
nav: '#404053',
secondary: '#9191A4',
badge: '#3F3F51',
'input-border': '#565666',
input: '#2A2A35',
},
fontFamily: {
poppins: ["'Poppins'", 'sans-serif'],
},
},
plugins: [],
}
This is my App.js
import { useEffect } from 'react'
function App() {
useEffect(() => {
document.body.classList.add('text-white', 'bg-color', 'font-poppins')
// 👇️ checking if body element contains a class
if (document.body.classList.contains('font-poppins')) {
console.log('body tag contains class')
}
}, [])
return <p className="text-white">Test</p>
}
export default App
and this is my index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import App from './App'
import AppNavbar from './components/AppNavbar'
import reportWebVitals from './reportWebVitals'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<React.StrictMode>
<AppNavbar /> //this is another component
<App />
</React.StrictMode>
)
When i start the application and check the console , the classes have been added to the body and p tags but the 'text-white' class isn't working.What am i doing wrong ?
When you put the colors or any other custom values also like fontFamily this will override tailwindcss values, that's why text-white isn't working, to fix this you need to insert your custom values inside extend like this :
extend: {
colors: {
body: "#DCDCDC",
"selected-text": "#A3A3FF",
theme: "#3F3FFF",
nav: "#404053",
secondary: "#9191A4",
badge: "#3F3F51",
"input-border": "#565666",
input: "#2A2A35",
},
fontFamily: {
poppins: ["'Poppins'", "sans-serif"],
},
},

Why I Can't use Leaflet Map on Nextjs even with Dynamic Import?

Leaflet need global window object and as you know on SSR there is no window and it will return an error window is not definded
I searched a lot and the only way to use Leaflet it was to use nextjs dynamic import but i get nothing In my Page
It's been a day I'm searching the web how to solve this and NOTHING worked so far for me.
I Really need an answer for this
Why Map component won't Render?
Map.tsx
import { useState } from "react";
import { MapContainer, TileLayer, Marker, useMapEvents } from "react-leaflet";
import "leaflet/dist/leaflet.css";
import "leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css";
import "leaflet-defaulticon-compatibility";
const Map = () => {
const [position, setPosition] = useState<[number, number]>([51.505, 51.505]);
// Add Marker on Map onClick
function Mark() {
const map = useMapEvents({
click: ({ latlng }) => {
setPosition([latlng.lat, latlng.lng]);
},
});
return <Marker position={position} />;
}
return (
<MapContainer
center={[35.7219, 51.3347]}
zoom={6}
scrollWheelZoom={false}
style={{ height: "50vh" }}
>
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
<Mark />
</MapContainer>
);
};
export default Map;
my dynamic import
import dynamic from "next/dynamic";
const AdPage = ({ ad }: AdPageProps): JSX.Element => {
const MapWithNoSSR = dynamic(() => import("./NewAd/map"), {
ssr: false,
});
return (
<>
<MapWithNoSSR />
</>
);
};
export default AdPage;
In my app I use separate index.ts file in the same folder as Map component to import map dynamically
index.ts
import dynamic from "next/dynamic";
export default dynamic(() => import("./Map"), { ssr: false });
and then use simple import where I need this map
import CustomMap from "../Map";

How to call function in child functional component

I am working on react project which uses mapbox-gl-js and I have a question. How can I call function from my child component, so it will be executed in my parent component. I need this because my child component has UI for my mapbox map and I need to perform map-related stuff(eg. execute map.flyTo function). I will use mapbox-example-react project to show what I want to do, because it has similar structure as my main complicated project. Tried to use useRef and forwardRef hooks to make it work but had no success.
import React, { useRef, useEffect, useState, forwardRef } from 'react';
import mapboxgl from 'mapbox-gl';
import './Map.css';
import TestJs from './Test';
mapboxgl.accessToken =
'pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4M29iazA2Z2gycXA4N2pmbDZmangifQ.-g_vE53SD2WrJ6tFX7QHmA';
const Map = () => {
const mapContainerRef = useRef(null);
const map = useRef();
const [lng, setLng] = useState(5);
const [lat, setLat] = useState(34);
const [zoom, setZoom] = useState(1.5);
// Initialize map when component mounts
useEffect(() => {
map.current = new mapboxgl.Map({
container: mapContainerRef.current,
style: 'mapbox://styles/mapbox/streets-v11',
center: [lng, lat],
zoom: zoom
});
// Add navigation control (the +/- zoom buttons)
map.current.addControl(new mapboxgl.NavigationControl(), 'top-right');
map.current.on('move', () => {
setLng(map.current.getCenter().lng.toFixed(4));
setLat(map.current.getCenter().lat.toFixed(4));
setZoom(map.current.getZoom().toFixed(2));
});
// Clean up on unmount
return () => map.current.remove();
}, []); // eslint-disable-line react-hooks/exhaustive-deps
const buttonref = useRef(test33);
function test33() {
map.current.flyTo({
center: [
1,
1,
],
zoom: 16,
essential: true
});
}
return (
<div>
<div className='sidebarStyle'>
<div>
Longitude: {lng} | Latitude: {lat} | Zoom: {zoom}
<br />
<TestJs ref={buttonref} />
</div>
</div>
<div className='map-container' ref={mapContainerRef} />
</div>
);
};
export default Map;
This is my child component, where I want to call my function.
import React, {forwardRef, useRef} from 'react'
const Test = forwardRef(() => {
return (
<button onClick={ref.current.test33}>Button</button>
)
})
export default Test
Link to mapbox example doc: https://docs.mapbox.com/help/tutorials/use-mapbox-gl-js-with-react/
Link to github mapbox github repo with example react-create-app: https://github.com/mapbox/mapbox-react-examples/tree/master/basic
Martin and azium were right in the comments. Using props to call this function worked.

Are there any working react examples of kepler.gl?

Right now there is an open, breaking bug in the provided kepler.gl React examples.
The open issue:
https://github.com/keplergl/kepler.gl/issues/983
The React examples that currently don't seem to work:
https://github.com/keplergl/kepler.gl/tree/master/examples
I'm trying to get a kepler.gl map working in my React app. I can see the sidebar and other extraneous things show up but the actual map itself does not. If anybody has any working examples of integrating a kepler map into a React app I would love to see it but also if anybody knows why my current integration is broken that would also be of great help.
In my index.js file I have:
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware, combineReducers, compose} from 'redux';
import { Provider } from 'react-redux'
import ReduxThunk from 'redux-thunk'
import * as serviceWorker from './serviceWorker';
import axios from 'axios';
import App from './components/App';
import config from './config';
// reducers
import auth from './reducers/auth';
import runtime from './reducers/runtime';
import navigation from './reducers/navigation';
import posts from './reducers/posts';
// import reducers from './reducers';
import keplerGlReducer from 'kepler.gl/reducers';
import {enhanceReduxMiddleware} from 'kepler.gl/middleware';
axios.defaults.baseURL = config.baseURLApi;
axios.defaults.headers.common['Content-Type'] = "application/json";
const token = localStorage.getItem('token');
if (token) {
axios.defaults.headers.common['Authorization'] = "Bearer " + token;
}
const reducers = combineReducers({
auth,
runtime,
navigation,
posts,
// <-- mount kepler.gl reducer in your app
keplerGl: keplerGlReducer,
});
const store = createStore(
reducers,
applyMiddleware(
ReduxThunk
)
);
ReactDOM.render(
<Provider store={store}>
<link rel="stylesheet" href="https://d1a3f4spazzrp4.cloudfront.net/kepler.gl/uber-fonts/4.0.0/superfine.css"></link>
<link href="https://api.tiles.mapbox.com/mapbox-gl-js/v1.1.1/mapbox-gl.css" rel="stylesheet"></link>
<script src="https://unpkg.com/kepler.gl/umd/keplergl.min.js"></script>
<App />
</Provider>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
serviceWorker.unregister();
And in my map component I have:
import React from 'react';
import {connect} from 'react-redux';
import KeplerGl from 'kepler.gl';
import {addDataToMap} from 'kepler.gl/actions';
import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer';
const sampleTripData = {
fields: [
{name: 'tpep_pickup_datetime', format: 'YYYY-M-D H:m:s', type: 'timestamp'},
{name: 'pickup_longitude', format: '', type: 'real'},
{name: 'pickup_latitude', format: '', type: 'real'}
],
rows: [
['2015-01-15 19:05:39 +00:00', -73.99389648, 40.75011063],
['2015-01-15 19:05:39 +00:00', -73.97642517, 40.73981094],
['2015-01-15 19:05:40 +00:00', -73.96870422, 40.75424576]
]
};
const sampleConfig = {
visState: {
filters: [
{
id: 'me',
dataId: 'test_trip_data',
name: 'tpep_pickup_datetime',
type: 'timeRange',
enlarged: true
}
]
}
};
class Maps extends React.Component {
componentDidMount() {
this.props.dispatch(
addDataToMap({
datasets: {
info: {
label: 'Sample Taxi Trips in New York City',
id: 'test_trip_data'
},
data: sampleTripData
},
option: {
centerMap: true,
readOnly: false
},
config: sampleConfig
})
);
}
render() {
return (
<div style={{position: 'absolute', left: 0, width: '100vw', height: '100vh'}}>
soimething
<AutoSizer>
{({height, width}) => (
<KeplerGl
id="map"
width={width}
mapboxApiAccessToken={"my mapbox token"}
height={height}
/>
)}
</AutoSizer>
</div>
);
}
}
const mapStateToProps = state => state;
const dispatchToProps = dispatch => ({dispatch});
export default connect(mapStateToProps, dispatchToProps)(Maps);
I don't see anything glaringly out of order in your example but if the UI is showing up and the map is not, it could be the Mapbpox-related imports and tokens etc. Would be good to inspect your console for errors or warnings and also check the network tab.
I imported a working example I have with Create React App plus Typescript and KeplerGL into Codesandbox for your reference if you still need it: https://codesandbox.io/s/create-react-app-typescript-keplergl-bv0vb
Save a .env file in root and give ur React_API=
it will work

Leaflet with next.js?

I am getting a ReferenceError:
window is not defined when using next.js with leaflet.js .
Wondering if there's a simple solution to this problem - is using next.js overcomplicating my workflow?
for those curious with the exact code,
import React, { createRef, Component } from "react";
import L from "leaflet";
import { Map, TileLayer, Marker, Popup, DivOverlay } from "react-leaflet";
import axios from "axios";
import Header from "./Header";
export default class PDXMap extends Component {
state = {
hasLocation: false,
latlng: {
lat: 45.5127,
lng: -122.679565
},
geoJSON: null
};
mapRef = createRef();
componentDidMount() {
this.addLegend();
if (!this.state.hasLocation) {
this.mapRef.current.leafletElement.locate({
setView: true
});
}
axios
.get(
"https://opendata.arcgis.com/datasets/40151125cedd49f09d211b48bb33f081_183.geojson"
)
.then(data => {
const geoJSONData = data.data;
this.setState({ geoJSON: geoJSONData });
return L.geoJSON(this.state.geoJSON).addTo(
this.mapRef.current.leafletElement
);
});
}
handleClick = () => {
this.mapRef.current.leafletElement.locate();
};
handleLocationFound = e => {
console.log(e);
this.setState({
hasLocation: true,
latlng: e.latlng
});
};
getGeoJsonStyle = (feature, layer) => {
return {
color: "#006400",
weight: 10,
opacity: 0.5
};
};
addLegend = () => {
const map = this.mapRef.current.leafletElement;
L.Control.Watermark = L.Control.extend({
onAdd: function(map) {
var img = L.DomUtil.create("img");
img.src = "https://leafletjs.com/docs/images/logo.png";
img.style.width = "200px";
return img;
}
});
L.control.watermark = function(opts) {
return new L.Control.Watermark(opts);
};
L.control.watermark({ position: "bottomleft" }).addTo(map);
};
render() {
const marker = this.state.hasLocation ? (
<Marker position={this.state.latlng}>
<Popup>
<span>You are here</span>
</Popup>
</Marker>
) : null;
return (
<Map
className="map-element"
center={this.state.latlng}
length={4}
onClick={this.handleClick}
setView={true}
onLocationfound={this.handleLocationFound}
ref={this.mapRef}
zoom={14}
>
<TileLayer
attribution='&copy OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{marker}
</Map>
);
}
}
/**
* TODO: Add Header + Legend to map
* - Header to be styled
* - Legend to be present in header
*
*/
import React from 'react';
import PDXMap from "../components/map";
export default function SignIn() {
const classes = useStyles();
return (
<PDXMap/>
);
}
I'm happy to use any way forward - just interested in getting a functional product.
Cheers!
Update
Hey everyone,
I am still getting this error (came back to this a bit later than I had planned haha).
I am currently using this approach with useEffects,
import React, {useEffect, useState} from 'react';
function RenderCompleted() {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true)
return () => {
setMounted(false)
}
});
return mounted;
}
export default RenderCompleted;
and this is the page it is showing on
import React, { useEffect } from "react";
import Router, { useRouter } from "next/router";
import { useRef, useState } from "react";
//viz
import PDXMap from "../../components/Visualization/GIS/map";
import RenderCompleted from "../../components/utils/utils";
// import fetch from 'isomorphic-unfetch';
import { Cookies, CookiesProvider } from "react-cookie";
const cookies = new Cookies();
//containers
// Layouts
import Layout from "../../components/Layout/Layout_example";
import Chart from "../../components/Visualization/Graphs/Chart";
import Table from "../../components/Visualization/Tables/Table";
import Sidebar from "../../components/Layout/Sidebar/SidebarProperty";
export default function Bargains() {
// const [inbrowser, setBrowser] = useState(false);
const choiceRef = useRef<any>();
const [message, setMessage] = useState<any>(null);
const [productList, setProductList] = useState<any>([]);
const [searched, setSearched] = useState(false);
const router = useRouter();
let token = cookies.get("token");
// useEffect(() => {
// setBrowser(true);
// });
const isMounted = RenderCompleted();
const columns = React.useMemo(
() => [
....
],
[]
)
async function handleChoice() {
console.log("searching...", choiceRef.current?.value);
setMessage("Searching...");
var headers = {
"Content-Type": "application/x-www-form-urlencoded",
"auth-token": token,
};
fetch(
....
}
<div className="flex flex-wrap ">
{isMounted && <PDXMap/>}
<Table columns={columns as any} data={productList as any} />
</div>
</div>
</div>
</div>
</Layout>
)
}
With the same error message of
ReferenceError: window is not defined
##update two
Okay, so oddly, it does work when I browse into the site from another page, but not when i load the page itself.
Will have a think on this, but perhaps it is because the map is loading data with componentDidMount() and that is interacting weirdly?
Update
Okay I've created a more simple example based on https://github.com/rajeshdh/react-leaflet-with-nextjs
Now it is loading, but the tiles are showing incorrectly, with some tiles not loading.
This is the map component I am using to be simple,
import React, { Component, createRef } from 'react';
import { Map, TileLayer, Marker, Popup, MapControl, withLeaflet } from 'react-leaflet';
import { GeoSearchControl, OpenStreetMapProvider } from 'leaflet-geosearch';
class SearchBox extends MapControl {
constructor(props) {
super(props);
props.leaflet.map.on('geosearch/showlocation', (e) => props.updateMarker(e));
}
createLeafletElement() {
const searchEl = GeoSearchControl({
provider: new OpenStreetMapProvider(),
style: 'bar',
showMarker: true,
showPopup: false,
autoClose: true,
retainZoomLevel: false,
animateZoom: true,
keepResult: false,
searchLabel: 'search'
});
return searchEl;
}
}
export default class MyMap extends Component {
state = {
center: {
lat: 31.698956,
lng: 76.732407,
},
marker: {
lat: 31.698956,
lng: 76.732407,
},
zoom: 13,
draggable: true,
}
refmarker = createRef(this.state.marker)
toggleDraggable = () => {
this.setState({ draggable: !this.state.draggable });
}
updateMarker = (e) => {
// const marker = e.marker;
this.setState({
marker: e.marker.getLatLng(),
});
console.log(e.marker.getLatLng());
}
updatePosition = () => {
const marker = this.refmarker.current;
if (marker != null) {
this.setState({
marker: marker.leafletElement.getLatLng(),
});
}
console.log(marker.leafletElement.getLatLng());
}
render() {
const position = [this.state.center.lat, this.state.center.lng];
const markerPosition = [this.state.marker.lat, this.state.marker.lng];
const SearchBar = withLeaflet(SearchBox);
return (
<div className="map-root">
<Map center={position} zoom={this.state.zoom} style={{
height:"700px"
}}>
<TileLayer
attribution='&copy OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker
draggable={true}
onDragend={this.updatePosition}
position={markerPosition}
animate={true}
ref={this.refmarker}>
<Popup minWidth={90}>
<span onClick={this.toggleDraggable}>
{this.state.draggable ? 'DRAG MARKER' : 'MARKER FIXED'}
</span>
</Popup>
</Marker>
<SearchBar updateMarker={this.updateMarker} />
</Map>
<style jsx>{`
.map-root {
height: 100%;
}
.leaflet-container {
height: 400px !important;
width: 80%;
margin: 0 auto;
}
`}
</style>
</div>
);
}
}
And to call it I am using this,
const SimpleExample = dynamic(() => import("../../components/Visualization/GIS/map"), {
ssr: false
});
And have tried this
{isMounted && }
Answer for 2020
I also had this problem and solved it in my own project, so I thought I would share what I did.
NextJS can dynamically load libraries and restrict that event so it doesn't happen during the server side render. See the documentation for more details.
In my examples below I will use and modify example code from the documentation websites of both NextJS 10.0 and React-Leaflet 3.0.
Side note: if you use TypeScript, make sure you install #types/leaflet because otherwise you'll get compile errors on the center and attribution attributes.
To start, I split my react-leaflet code out into a separate component file like this:
import { MapContainer, Marker, Popup, TileLayer } from 'react-leaflet'
import 'leaflet/dist/leaflet.css'
const Map = () => {
return (
<MapContainer center={[51.505, -0.09]} zoom={13} scrollWheelZoom={false} style={{height: 400, width: "100%"}}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={[51.505, -0.09]}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
</MapContainer>
)
}
export default Map
I called that file map.tsx and placed it in a folder called components but you might call it map.jsx if you don't use TypeScript.
Note: It is important that this code is in a separate file from where it is embedded into your page because otherwise you'll still get window undefined errors.
Also Note: don't forget to specify the style of the MapContainer component so it doesn't render as zero pixels in height/width. In the above example, I added the attribute style={{height: 400, width: "100%"}} for this purpose.
Now to use that component, take advantage of NextJS's dynamic loading like this:
import dynamic from 'next/dynamic'
function HomePage() {
const Map = dynamic(
() => import('#components/map'), // replace '#components/map' with your component's location
{ ssr: false } // This line is important. It's what prevents server-side render
)
return <Map />
}
export default HomePage
If you want the map to be replaced with something else while it's loading (probably a good practice) you should use the loading property of the dynamic function like this:
import dynamic from 'next/dynamic'
function HomePage() {
const Map = dynamic(
() => import('#components/map'), // replace '#components/map' with your component's location
{
loading: () => <p>A map is loading</p>,
ssr: false // This line is important. It's what prevents server-side render
}
)
return <Map />
}
export default HomePage
Adrian Ciura commented on the flickering which may occur as your components re-render even when nothing about the map should change. They suggest using the new React.useMemo hook to solve that problem. If you do, your code might look something like this:
import React from 'react'
import dynamic from 'next/dynamic'
function HomePage() {
const Map = React.useMemo(() => dynamic(
() => import('#components/map'), // replace '#components/map' with your component's location
{
loading: () => <p>A map is loading</p>,
ssr: false // This line is important. It's what prevents server-side render
}
), [/* list variables which should trigger a re-render here */])
return <Map />
}
export default HomePage
I hope this helps. It would be easier if react-leaflet had a test for the existence of window so it could fail gracefully, but this workaround should work until then.
window is not available in SSR, you probably get this error on your SSR env.
One way to solve this is to mark when the component is loaded in the browser (by using componentDidMount method), and only then render your window required component.
class MyComp extends React.Component {
state = {
inBrowser: false,
};
componentDidMount() {
this.setState({ inBrowser: true });
}
render() {
if (!this.state.inBrowser) {
return null;
}
return <YourRegularComponent />;
}
}
This will work cause componentDidMount lifecycle method is called only in the browser.
Edit - adding the "hook" way
import { useEffect, useState } from 'react';
const MyComp = () => {
const [isBrowser, setIsBrowser] = useState(false);
useEffect(() => {
setIsBrowser(true);
}, []);
if (!isBrowser) {
return null;
}
return <YourRegularComponent />;
};
useEffect hook is an alternative for componentDidMount which runs only inside the browser.
Any component importing from leaflet or react-leaflet should be dynamically imported with option ssr false.
import dynamic from 'next/dynamic';
const MyAwesomeMap = dynamic(() => import('components/MyAwesomeMap'), { ssr: false });
Leaflet considers it outside their scope, as they only bring support on issues happening in vanilla JS environment, so they won't fix (so far)
Create file loader.js, place the code below :
export const canUseDOM = !!(
typeof window !== 'undefined' &&
window.document &&
window.document.createElement
);
if (canUseDOM) {
//example how to load jquery in next.js;
window.$ = window.jQuery = require('jquery');
}
Inside any Component or Page import the file
import {canUseDOM} from "../../utils/loader";
{canUseDOM && <FontAwesomeIcon icon={['fal', 'times']} color={'#4a4a4a'}/>}
or Hook Version
import React, {useEffect, useState} from 'react';
function RenderCompleted() {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true)
return () => {
setMounted(false)
}
});
return mounted;
}
export default RenderCompleted;
Invoke Hook:
const isMounted = RenderCompleted();
{isMounted && <FontAwesomeIcon icon={['fal', 'times']} color={'#4a4a4a'}/>}
Tiles are showing incorrectly because you miss leaflet.css. Download it from official website and just add to your project. Suddendly the map tiles will be shown correctly. Well, you won't see anything because default container size is 0,0. To fix it give a size to the container adding this on your styles.css:
.leaflet-container {
height: 450px;
width: 100%;
}
most effective approach is suggested by #FlippingBinary I just need to argue to use little different useMemo functionality. Since useMemo is not consistent to prevent re-render when is used inside function you have to use it separately. This only works form me
vloz.tsx === Page
import React, { Suspense, lazy } from 'react';
const Vloz: React.FC = () => {
const [position, setPosition] = useState<number[] | undefined>();
const AddAd = lazy(() => import('#components/maps/AddAd'));
const MemoMap = React.useMemo(() => {
return (
<Suspense fallback={<p>Loading</p>}>
<AddAd position={position as LatLngTuple} setPosition={setPosition} />
</Suspense>
);
}, [position]);
return (
<div>
{MemoMap}
</div>
);
};
other component is normally used as other tutorials suggest.
This approach will keep Map component render til position change
let divIcon, Map, TileLayer, Marker, Popup;
// first you must check your environment
if (process.browser) {
// then you can import
divIcon = require('leaflet').divIcon;
Map = require('react-leaflet').Map;
TileLayer = require('react-leaflet').TileLayer;
Marker = require('react-leaflet').Marker;
Popup = require('react-leaflet').Popup;
}
In case anyone has a situation that the map component is shared between all pages and you have problem that map component re-renders when route changes (when switching between pages) you must load your map component in _app.js (using techniques explained in the accepted answer) and also consider using shallow routing
We need to use this component without ssr. In this, we will make use of the next js dynamic feature. If you do a set state operation on the map with event handlers, for example, when you click the marker, the map will be rerender. You will need to use react.useMemo for avoid unnecessary re-rendering.
const MapWithNoSSR = React.useMemo(() => dynamic(() => import('../components/Map'), {
loading: () => <p>A map is loading</p>,
ssr: false,
}), []);

Categories

Resources