Let me summarize the app I am working on :
I am creating a React app for finding a soccer club depending on your age, gender and the city where you live.
It works fine for now, but I want the map to center the view on the new markers when they appear in the results.
I've read that we have to initialize the center with {WhenCreated} on the MapContainer, and use useMapEvents to handle the update of the view but I can't manage to nest it or I loose the range of my array of marker..
Here is my code :
import React, { useState, useEffect } from "react";
import Header from "../header/Header";
import "./Moible3.css";
import { MapContainer, TileLayer, Marker, Popup, useMapEvents } from "react-leaflet";
import TextField from "@mui/material/TextField";
import Autocomplete from "@mui/material/Autocomplete";
import FormControlLabel from "@mui/material/FormControlLabel";
import FormControl from "@mui/material/FormControl";
import Radio from "@mui/material/Radio";
import RadioGroup from "@mui/material/RadioGroup";
import btnPicture from "../../assets/Boutons/buttontransparent.png";
import axios from "axios";
import Geolocalisation from "../Hook/Geolocalisation";
import MarkerClusterGroup from "react-leaflet-markercluster";
import Footer from '../../components/Footer/Footer.jsx';
import L from "leaflet";
function Mobile3() {
const [allcities, setallcities] = useState([]);
const [categories, setCategories] = useState([]);
const [allteams, setallTeams] = useState([]);
const [clubSearch, setclubSearch] = useState([]);
const [formData, setformData] = useState({
age: null,
city: "",
type: 'Libre',
gender2: '',
category: "",
})
const filterSearch = (e) => {
e.preventDefault();
let categorieWanted = categories.filter(
(categorySelected) =>
(formData.gender2 === categorySelected.gender
&& formData.age >= categorySelected.minAge && formData.age <= categorySelected.maxAge && formData.type === categorySelected.type))
const resultofSearch = allteams.filter((clubWanted) =>
clubWanted.Category === categorieWanted[0].name && clubWanted.Localite === formData.city);
setclubSearch(resultofSearch);
console.log(resultofSearch)}
const handleChange = (e) => {
setformData({ ...formData, [e.target.name]: e.target.value })
}
useEffect(() => {
axios.get("https://api-clubs-cvl.herokuapp.com/cities")
.then((res) => {
let result = [];
res.data.forEach(element => {
result.push({ label: element.name })
});
setallcities(result)
})
}, []);
useEffect(() => {
axios.get("https://api-clubs-cvl.herokuapp.com/categories")
.then((res) => setCategories(res.data))
}, [])
useEffect(() => {
axios.get("https://api-clubs-cvl.herokuapp.com/allteams").then((res) => setallTeams(res.data))
}, [])
return (
<div className="fullPage">
<Header />
<div className="subContainer">
<h1 className="titlePart1">Trouvez un club près </h1>
<h1 className="titlePart2">de chez vous ! </h1>
<main className="mapContainer">
<MapContainer
className="mapLeaflet"
center={[48.856614, 2.3522219]}
zoom={13}
scrollWheelZoom={true}
doubleClickZoom={true}
zoomControl={true}
>
<TileLayer
attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Geolocalisation />
<MarkerClusterGroup
animate={true}
onClusterClick={(cluster) =>
console.warn(
"cluster-click",
cluster,
cluster.layer.getAllChildMarkers()
)
}
>
{clubSearch.length !== 0 ?
clubSearch.slice(0, 100).map((res, index2) => {
return (
<Marker key={index2} position={[res.Latitude, res.Longitude]} >
<Popup key={index2}>
<p> {res.Club}</p>
</Popup>
</Marker>)
})
: null
}
</MarkerClusterGroup>
</MapContainer>
</main>
<div className="legendAndForm">
<section className="legendMap">
<p className="legend">
Entrez votre <em className="birthday">date de naissance </em>et la{" "}
<em className="ranked"> compétition </em> souhaitée pour découvrir
les clubs à proximité !{" "}
</p>
</section>
<div className="filtrations">
<form className="filtrationsWrapper" onSubmit={(e) => filterSearch(e)}>
<div className="filtre1">
<span className="filterTitle1">VOTRE ÂGE </span>
<TextField
label="Âge"
type="number"
margin="normal"
name="age"
onChange={(e) => { handleChange(e) }}
helperText="Renseingez votre aĝe ici"
focused
inputProps={{
inputMode: "numeric",
pattern: "[0-9]*",
placeholder: "10, 15, 30...",
}}
/>
</div>
<div className="filtre2">
<FormControl component="fieldset">
<span className="filterTitle2">COMPETITION :</span>
<RadioGroup
row
aria-label="gender"
defaultValue="female"
name="gender2"
error="Vous devez renseigner une compétition"
onChange={(e) => handleChange(e)}
required={true}
>
<FormControlLabel
value="Male"
className="radio1"
control={<Radio />}
label="Masculine"
/>
<FormControlLabel
className="radio1"
value="Female"
control={<Radio />}
label="Feminine"
/>
</RadioGroup>
</FormControl>
</div>
<div className="filtre3">
<span className="filterTitle3"> VOTRE VILLE </span>
<Autocomplete
disablePortal
id="combo-box-demo"
inputValue={formData.city}
options={allcities}
noOptionsText="Pas d'élement correspondant"
onInputChange={(event, newInputValue) => {
setformData({ ...formData, city: newInputValue });
}}
sx={{ width: 250 }}
renderInput={(params) => (
<TextField {...params} label="Rechercher" />
)}
/>
<div className="hiddenSearchResult" id='hidden'>
<p className="resultError"> Nique ta mère</p>
</div>
</div>
<div className="btnContainer" id='test'>
<button className="btnBackground" type="submit">
<img
className="findclubBtn"
alt="trouvez votre club"
src={btnPicture}
/>
</button>
</div>
</form>
</div>
<div className="resul">
{clubSearch.length !==0 ?
clubSearch.map((clubSelected, Uniqueindex) => {
return (
<div className="cardResult" key={Uniqueindex}>
<div className="titleContainer">
<span className="titleCard">
{clubSelected.name}
</span>
</div>
<div className="columnContainer">
<div className="column1">
<div className="logo1"></div>
<div className="logo2"></div>
<div className="logo3"></div>
</div>
<div className="column2">
<div className="info1"> {clubSelected.Mail}</div>
<div className="info2">{clubSelected.Adresse}</div>
<div className="info3">Voir plus d'infos</div>
</div>
</div>
</div>)
})
: null }
</div>
</div>
<Footer/>
</div>
</div>
);
}
export default Mobile3;
You can find a gist here : https://gist.github.com/ManuelWCS/4e67a75fc9a1053ed6f1ff095225ea0c
If you want to see a demo click here
Thanks for your reading, please hit me up if you have questions or solutions !
:)
You do not need useMapEvents
. You can do it alternatively using the map instance only.
Take the map instance first using whenCreated
prop
Inside filterSearch
function check for two cases. Once if resultofSearch array is empty there are no available locations, then you need to return and alert something.
Otherwise, use all the coordinates from the available markers and zoom the map around all these coordinates. You can achieve that using fitBounds Leaflet method.
const [map, setMap] = useState(null);
...
<MapContainer whenCreated={setMap} />
...
const filterSearch = (e) => {
...
if (resultofSearch.length === 0)
return alert("There are no available locations");
const arrayOfLatLngs = resultofSearch.map(({ Latitude, Longitude }) => [
Latitude,
Longitude
]);
const bounds = L.latLngBounds(arrayOfLatLngs);
if (map) map.fitBounds(bounds);
}
Demo with part of your app