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("") .then((res) => { let result = []; res.data.forEach(element => { result.push({ label: element.name }) }); setallcities(result) }) }, []); useEffect(() => { axios.get("") .then((res) => setCategories(res.data)) }, []) useEffect(() => { axios.get("").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="">OpenStreetMap</a> contributors' url="" /> <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 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;If you want to see a demo click here
Thanks for your reading, please hit me up if you have questions or solutions !
:)
1 Answer
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
3