简体   繁体   中英

React + leaflet + routing machine : Problem accessing map outside MapContainer

I'm having trouble with my project, I want to draw a route and add markers on my map, but the event is triggered outside in a sidenav how collect the latitude and longitude of the marker. I can't call my map outside as I'm having the message:

useLeafletContext() can only be used in a descendant of

Architecture of the project: Apps

  • App
    • SideBar
    • Map
      • MapContainer
        • RoutingAdd(Where I need to create route)

Routing.js

export default function RoutingAdd(props) {
const map = useMap();
//const dispatch = useDispatch();
const wayponts = useSelector((state) => state.startingPoints.value);
useEffect(() => {
    if (!map) return;
    console.log(wayponts);
    const routingControl = L.Routing.control({
        waypoints: [
            L.latLng(wayponts.start.lat, wayponts.start.long),
            L.latLng(wayponts.start.lat, wayponts.start.long)
        ],
        routeWhileDragging: true
    }).addTo(map);

    return () => map.removeControl(routingControl);
}, [map]);

return null;

}

Map.js

function Map(props) {
const position = [48.856614, 2.3522219]
return (
    <MapContainer style={{ width: "100%", height: "100vh", zIndex: 0 }}  center={position} zoom={13} >
        <TileLayer
            attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
            url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />
        <BornesCluster />
        <RoutingAdd addStart={props.handleNewStart} addEnd={props.handleNewEnd}></RoutingAdd>
    </MapContainer>
);

} export default Map;

Sidebar.js

function IntputNavigtion() {
    const dispatch = useDispatch();
    const map = useMap();
    function handleStartPoint(event) {
        console.log(event);
        //map.panTo(new LatLng(event.value[0], event.value[1], 0));
        dispatch(createStart({start:{lat: event.value[0], long: event.value[1]}}));
    }
    return (
        <div>
            <form>
                <select className="form-select" aria-label="Default select example">
                    <option selected>Choisir un modèle de voiture</option>
                    <option value="1">Telsa Model 3</option>
                    <option value="2">Zoe</option>
                </select>
                <hr />
                <InputAddresseGeocode
                    label="Point de départ"
                    placeholder="Entrer un point de départ"
                    valueSelectedCall={handleStartPoint}
                ></InputAddresseGeocode>
                <InputAddresseGeocode label="Point d'arrivé" placeholder="Entrer une addresse d'arrivé"></InputAddresseGeocode>
            </form>

            <button className="btn btn-primary mt-2">
                Calculer
            </button>
        </div>
    );
}

function SideBar() {

    const [toggled, setToggled] = useState('toggled');
    function toggleSideBar() {
        if(toggled == 'toggled') setToggled('hide')
        else setToggled('toggled')
    }

    function handleStartUpdate() {

    }

    function handleEndUpdate() {

    }

    return (
        <main>
            <button className={'btn btn-primary button-toggle button-toggle-' + toggled} onClick={toggleSideBar}><i className="fa fa-solid fa-bars"></i></button>
            <div id="wrapper" className={'d-flex flex-column flex-shrink-0 p-3 bg-light ' + toggled} style={{width: '360px', position: 'fixed', zIndex: '1',
                height: '100%', right: '0', top: '0'}}>
                <a href="/" className="d-flex align-items-center mb-3 mb-md-0 me-md-auto link-dark text-decoration-none">
                    <span className="fs-4">Navigation</span>
                </a>
                <hr />
                <IntputNavigtion />
            </div>
        </main>
    );
}

export default SideBar;

If anyone has an idea to communicate between these two components or to rethink about how to create the route, thank-you

The only way you can useMap() is inside the <MapContainer> . However you can pass a ref to the map from inside to the outside. Since your components are really nested, you'll have to pass it from component to component, which isn't great, and you can avoid doing that by creating a new Context or using a global state manager like zustand or redux . However im gonna tell you the quick and dirty way to do this.

in App.js

import { useRef } from "react"
function App(props) {
const mapRef = useRef();

return (
  <div>
   ...
   <Sidebar mapRef={mapRef}/>
   <Map mapRef={mapRef}/>
   ...
  </div>
)
}

in Map.js

...
<RoutingAdd mapRef={props.mapRef} addStart={props.handleNewStart} addEnd={props.handleNewEnd}/>
...

in Routing.js

export default function RoutingAdd(props) {
const map = useMap();
props.mapRef.current = map;
...

in Sidebar.js

function SideBar(props) {
  const map = props.mapRef.current
...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM