Using react-leaflet-geosearch in React Leaflet: cannot read property addControl - reactjs

Trying to add react-leaflet-geosearch to my leaflet map and I get the error:
Cannot read property 'addControl' of undefined
I do not know what the problem seems to be:
My code:
render() {
const prov = OpenStreetMapProvider();
const GeoSearchControlElement = SearchControl;
return (
<>
<MapContainer
style={{ height: "100%", width: "100%" }}
center={position}
zoom="0"
scrollWheelZoom={true}
>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://api.mapbox.com/styles/v1/<style>/tiles/256/{z}/{x}/{y}#2x?access_token=<token>"
/>
<Marker position={position} ></Marker>
<ChangeMapView coords={position} />
<GeoSearchControlElement
provider={prov}
showMarker={true}
showPopup={false}
popupFormat={({ query, result }) => result.label}
maxMarkers={3}
retainZoomLevel={false}
animateZoom={true}
autoClose={false}
searchLabel={"Enter address, please"}
keepResult={true}
/>
</MapContainer>
</>
);

You seem to use React Leaflet version 3 since you have a <MapContainer> component.
Unfortunately, the react-leaflet-geosearch plugin you are trying to integrate was made for React Leaflet version 2: https://github.com/TA-Geoforce/react-leaflet-geosearch/blob/master/package.json#L108
Due to the big API changes between these major versions, it is unlikely it will be compatible.

react-leaflet has been re-designed and useLeaflet hook is no longer available in v3 API which makes SearchControl component not compatible with v3
Until react-leaflet-geosearch compatibility with react-leaflet v3 is implemented, the following SearchControl could be considered instead (compatible with react-leaflet v3)
const SearchControl = (props) => {
const map = useMap();
useEffect(() => {
const searchControl = new GeoSearchControl({
provider: props.provider,
...props,
});
map.addControl(searchControl);
return () => map.removeControl(searchControl);
}, [props]);
return null;
};
Usage
class MapComponent extends React.Component {
render() {
const { position, zoom } = this.props;
const prov = OpenStreetMapProvider();
return (
<MapContainer center={position} zoom={zoom}>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='© OpenStreetMap contributors'
/>
<SearchControl
provider={prov}
showMarker={true}
showPopup={false}
popupFormat={({ query, result }) => result.label}
maxMarkers={3}
retainZoomLevel={false}
animateZoom={true}
autoClose={false}
searchLabel={"Enter address, please"}
keepResult={true}
/>
</MapContainer>
);
}
}
Live demo

Related

React leaflet cannot update state

Building a React Leaflet app for first time, what I am trying to do is an onclick event on a layer that would update my state, the idea would be that the coordinates I add will modify state and I can then use this array to form a polygon. For some reasons I cannot make it work at the beginning of the project, this is the simplified code.
When clicking on one of the layers, I can console log all the information of the layer from the event no problem but cannot modify my state so I am thinking I might be rerendering. Can anyone spot the mistake? Thanks
const [coords, setCoords] = useState([])
const onclickevent = e => {
setCoords([...coords, e.target])
}
let givedata = (country, layer) => {
layer.on({click: onclickevent
});
};
<MapContainer className='leaflet-container' center={[48.85078290000001, 2.27537]} zoom={13} style={{ height: "100vh" }}>
<GeoJSON data={quartiersgeojson.features} onEachFeature={givedata}/>
<TileLayer
attribution='&copy OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
</MapContainer>

Next js with react-leaflet window is not defined when refreshing page

im using react-leaflet in Next js but when reloading page shows "window is not defined" even i am using dynamic import with ssr:false,
i saw this question made by others here and tried answers that they offered but didn't work, also tried to make the map mounted after component but again no result,
my code:
function ContactPage() {
const MapWithNoSSR = dynamic(() => import('../Map/Map'), {
ssr: false,
})
return (
<div className={styles.map}>
<MapWithNoSSR/>
</div>
)
}
function Map(){
const [markers, setMarkers]= useState([
{cord:[12.3774729,20.446257]},
{cord:[45.3774729,10.45224757]},
{cord:[40.3774729,15.4364757]},
])
<MapContainer center={[12.374729,22.4464757]} zoom={13} scrollWheelZoom={true} style={{height: "100%", width: "100%",zIndex:'1'}}>
<TileLayer
url={`example.com`}
attribution='Map data © <a>Mapbox</a>'
/>
{markers?.map((element,idx)=>{
return <Marker
position={element?.cord}
draggable={true}
animate={true}
key={idx}
>
<Popup>
Test PopUP
</Popup>
</Marker>
})}
</MapContainer>
}}
}
as you were told in the comment, dynamic () has to go outside of the component or screen you are going to return, e. g.
import dynamic from "next/dynamic"
const MyAwesomeMap = dynamic(() => import("../components/Map/index"), { ssr:false })
export default function inicio() {
return(
<>
<p>Example Map</p>
<MyAwesomeMap />
</>
)
}
Your Map component doesn't return anything

React-Leaflet: Polyline does not change colour despite colour value in Redux store updating

I have setup a Redux store which contains a hex colour code. I plan to implement a function where users can select a colour which the line will appear as. However, when the I used the selector to update the color prop of the polyline, the line on the map itself does not change colour. I have already verified the redux store is working fine.
const DashboardLineAnimation: React.FC<Props> = (): ReactElement => {
const [start, setStart] = useState([0, 0]);
const [end, setEnd] = useState([45, 45]);
return (
<div>
<input onChange={() => {setStart([start[0] + 10, start[1] + 10])}}></input>
<Polyline color={useSelector((state: RootState): string => state.attackMap.options.color.hex)} positions={[start as LatLngTuple, end as LatLngTuple]} />
</div>
);
};
You have to use the documented prop pathOptions which is an object the property color on that object and change the value of that property. color prop is available but normally it should be immutable as it is not documented on the official docs while pathOptions is documented as mutable, thus can be changed.
Also make sure the Polyline is a child of MapContainer when you use it. Here is a demo without using redux with a local state variable
function App() {
...
const [color, setColor] = useState("blue");
const handleClick = () => setColor("red");
return (
<>
<button onClick={handleClick}>Change polyline color</button>
<MapContainer center={position} zoom={13} style={{ height: "100vh" }}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Polyline pathOptions={{ color }} positions={polyline} />
</MapContainer>
</>
);
}

How pass props to React Leaflet.markercluster options since v2.0.0?

I tried to pass property with <MarkerClusterGroup {...markerclusterOptions}>
by let markerclusterOptions but i think it is not a good way, because it does not work!
in documentation (# React leaflet markercluster v2.0.0):
Since now you don't need to use options prop to pass Leaflet.markercluster option into <MarkerClusterGroup />.
I do not understand how do to that.
import React from 'react';
import MarkerClusterGroup from 'react-leaflet-markercluster';
let markerclusterOptions: {
maxClusterRadius: 10;
spiderfyDistanceMultiplier: 2;
spiderfyOnMaxZoom: true;
showCoverageOnHover: false;
zoomToBoundsOnClick: true;
};
// == Composant
const Map: React.FC<Props> = ({ lights }) => (
<div className="map">
<MapContainer
center={[46.362205, 1.523151]}
zoom={5}
minZoom={2}
>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png?api_key=******"
/>
<SetViewOnClick />
<MarkerClusterGroup {...markerclusterOptions}>{
lights.map((light) => (
<Marker
key={light.id}
position={[light.latitude, light.longitude]}
icon={iconPerson}
>
<Tooltip>
Name: {light.user_description} (I{light.index}.G{light.group})
<br />
Power: {light.power_level} %
<br />
Rf Quality: {light.rfquality}/5
</Tooltip>
</Marker>
))
}
</MarkerClusterGroup>
</MapContainer>
</div>
);
export default Map;
Most probably a typo:
let markerclusterOptions: { // with colon ":" you declare a type
maxClusterRadius: 10;
spiderfyDistanceMultiplier: 2;
spiderfyOnMaxZoom: true;
showCoverageOnHover: false;
zoomToBoundsOnClick: true;
}; // no assignment, value is undefined
Should have been:
let markerclusterOptions = { // with equal you assign a value
maxClusterRadius: 10,
spiderfyDistanceMultiplier: 2,
spiderfyOnMaxZoom: true,
showCoverageOnHover: false,
zoomToBoundsOnClick: true,
};

zoom in dynamically to fit all the marker React-leaflet

I'm using react-leaflet. to show the map in my react app. I'm also showing marker on the map too. The problem is the zoom level is not appropriate because sometimes the marker might be quite near to each other and sometimes they will be very far apart. Is there any way to set the zoom level depending on the distance of the markers so that the user can see all the markers at once?
Here is my code
<Map center={center} maxZoom={9} zoom={5}>
<MarkerClusterGroup showCoverageOnHover={false}>
{
markers.map(({fillColor, position, id}) =>
<CircleMarker fillColor={fillColor} color={darken(0.1, fillColor)} radius={10} fillOpacity={1} key={id} center={position} onClick={this.onClick} />
}
</MarkerClusterGroup>
</Map>
P.S: My react-leaflet version is 2.4.0
Assuming MarkerClusterGroup is a component from react-leaflet-markercluster package, the following example demonstartes how to auto-zoom to cover visible markers:
function CustomLayer(props) {
const groupRef = useRef(null);
const { markers } = props;
const mapContext = useLeaflet();
const { map} = mapContext; //get map instance
useEffect(() => {
const group = groupRef.current.leafletElement; //get leaflet.markercluster instance
map.fitBounds(group.getBounds()); //zoom to cover visible markers
}, []);
return (
<MarkerClusterGroup ref={groupRef} showCoverageOnHover={false}>
{markers.map(({ fillColor, position, id }) => (
<CircleMarker
fillColor={fillColor}
radius={10}
fillOpacity={1}
key={id}
center={position}
/>
))}
</MarkerClusterGroup>
);
}
Usage
function MapExample(props) {
const { markers, center } = props;
return (
<Map center={center} maxZoom={9} zoom={5}>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='© OpenStreetMap contributors'
/>
<CustomLayer markers={markers} />
</Map>
);
}

Resources