3

I have the following functional react component which correctly displays two static markers within a 'bounds' box which fits both markers inside.

I would like to be able to pass in an array of latitude and longitude values for the map to display but I can't work out how to do it.

This is the working, static example:

import React from 'react'
import { MapContainer, TileLayer, Marker } from 'react-leaflet'
import L from 'leaflet'
import 'leaflet/dist/leaflet.css'

const MapLeaflet = () => {

// STATIC MARKER POSITIONS
const position = [42.2974279, -85.628292];
const position2 = [-8.852507, -45.351563];

// BOUNDS CODE
const bounds = L.latLngBounds([position, position2]);

return (
    <MapContainer
        className=" map"
        center={position}
        bounds={bounds}
    >
        <Marker key={key} position={position}>
            <Heart/>
        </Marker>
        <Marker key={key} position={position2}>
            <Heart/>
        </Marker>

        <TileLayer
            attribution='&amp;copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
            url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />
    </MapContainer>
        
)
}

If I pass is {coords} I can then dynamically display the markers:

const MapLeaflet = ({coors}) => {
...
    { coords && coords.map(coord => (
           <Marker key={key} latitude={coord[0]} longitude={coord[1]}>
                <SomeMarker/>
           </Marker>
    ))}
...
}

But obviously, the map is not yet taking these 'coords' into consideration for the bounds. The console.log output of the passed in coords array is as follows:

0: (2) [51.52167056034225, -0.12894469488176763]
1: (2) [46.58635156377568, 2.1796793230151184]
2: (2) [40.819721, 14.341111]

Somehow I need to replace the following line with a reference to the passed in coords in a format the code accepts, but I can't work out how to do it.

const bounds = L.latLngBounds([position, position2]);

to something like

const bounds = L.latLngBounds({coords});

Any help would be very much appreciated.

Kind regards, Matt

Ray Purchase
  • 681
  • 1
  • 11
  • 37
  • Are you passing an array of 3 arrays (coords value)? `latLngBounds` accepts a pair of arrays of latlngs. Also why using {} on `latLngBounds`. coords value is not an object but an array of arrays instead – kboul Mar 28 '21 at 11:51
  • Hi kboul, the console log output just shows three entries as an example. I am using an array of arrays though I think, by the looks of it. The {coords} was just there as an example, I'm aware that it signifies an object. Do you have any idea if this is possible please? – Ray Purchase Mar 28 '21 at 13:10
  • Could you please create a small demo with your current attempt and the real values you are passing because it is not clear to me what you are trying at the moment? – kboul Mar 28 '21 at 13:20
  • Sorry, I haven't been very clear. I'll make a jsfiddle and comment when it's done. In the meantime, I just want to replace the static marker values, const position and const position2, with dynamic values I will pass in to the component as props. The dynamic values are being pulled from Firestore and look like the console.log I posted above. Thanks for your help! – Ray Purchase Mar 28 '21 at 13:26

1 Answers1

5

I think I understood what you want to achieve.

maxBounds is immutable in react-leaflet v.3 therefore you need to create a custom component that will change the map bounds upon coords change. It will take coords as prop and it will change the map bounds when coords change or when the comp lands.

function Bounds({ coords }) {
  const map = useMap();
  useEffect(() => {
    if (!map) return;

    map.fitBounds(coords);
  }, [map, coords]);
  return null;
}

In your app comp I included a case where bounds change (coords variable) and the map bounds change accordingly. Hopefully this is what you are looking for.

function App() {
  const [coords, setCoords] = useState([
    [51.52167056034225, -0.12894469488176763],
    [46.58635156377568, 2.1796793230151184],
    [40.819721, 14.341111]
  ]);

  return (
    <>
      <MapLeaflet coords={coords} />
      <button
        onClick={() =>
          setCoords([
            [52.52167056034225, -0.12894469488176763],
            [47.58635156377568, 2.1796793230151184],
            [41.819721, 14.341111]
          ])
        }
      >
        Change coords
      </button>
    </>
  );
}

Demo

kboul
  • 13,836
  • 5
  • 42
  • 53
  • you're an absolute legend. That's fantastic. You understood me perfectly and provided a brilliant solution. Thank you so very much. I'm amazed you wrote all that code in the sandbox to show me, I'm so sincerely thankful. Have a wonderful day – Ray Purchase Mar 28 '21 at 14:03
  • There's a very strange issue with the code. I have linked a short video to show you. https://streamable.com/9ok78f Basically, it doesn't work if started from fresh with the dynamic code, but if we start with the marker references in the useState it works, and then we switch over to the dynamic props, it still works, until the page is reloaded, when it fails again – Ray Purchase Mar 29 '21 at 04:43
  • I don't see the part where you use MapLeafet and passing coords props. You delete the static variable but then coords should come as props. In the demo i pass coords from App comp. Are you sure it's still coming and not be undefined? And moreover do they have the correct structute as expected (array of arrays of numbers)? – kboul Mar 29 '21 at 05:16
  • 1
    Sorry, I've added a slower video. I start off using state for the markers, which of course works. I then delete the state and import 'coords' as props, and this works (so the variables have the correct structure - you can see the map markers change). I then cold refresh the page and I get the Bounds are not valid error. I then just go through the process again just to show. https://streamable.com/r6sqka – Ray Purchase Mar 29 '21 at 05:44
  • Sorry but I do not see the part where you do `MapLeaflet coords={...}`. Also try return from useeffect if also coords.length is 0. `If (!map || coords.length===0) return` inside Bounds comp – kboul Mar 29 '21 at 06:09
  • It's after 6 seconds of the video and at 47 seconds too . It seems to be something to do with initialising the bounds or initialising the component. Adding the line above caused the map to not render. – Ray Purchase Mar 29 '21 at 06:22
  • I don't mean the component code but the where you instantiate MapLeaflet in the App or anywhere else. I am looking for that line and the coords variable you are passing not MapLeaflet component itself – kboul Mar 29 '21 at 06:39
  • Oh, I'm sorry, ha. Here you go: https://cldup.com/B2-5wPzwV5.jpg Here's the code assigning the array from Firestore https://cldup.com/FFfYA0Z416.jpg – Ray Purchase Mar 29 '21 at 06:53
  • One possible error I can see is that you push to the coords array directly. You cannot do that. You are mutating the array. Have you cloned it first? Please log the coords value inside useeffect whete it says bounds not valid – kboul Mar 29 '21 at 06:59
  • Here's a screenshot: https://cldup.com/LJdBTEwF-Z.jpg The coords are there, which is strange. I removed the "|| coords.length===0" as this stops it from producing an error, but the map doesn't display – Ray Purchase Mar 29 '21 at 07:30
  • I modified the sandbox. I used a featureGroup to place there the markers without adding them to the map and get the bounds from the group. Could you try that approach? – kboul Mar 29 '21 at 08:24
  • Your code works perfectly, I still have the same problem using dynamic values. I know why - before when I was testing the original code I started (to do a proof on concept) with the static state values. The map worked with these. I then changed the component to accept the dynamic values ie. export default function MapLeaflet({coords}) - this worked because by this stage the dynamic coords values are stored in the system. If I try and do it dynamically from fresh the coords array is empty. There's a problem with how I'm calling and storing the coords data from Firestore – Ray Purchase Mar 29 '21 at 10:06
  • Thank you so much for all your time. We have identified the problem and it's not with your code at all, it's with my data fetching and storing. – Ray Purchase Mar 29 '21 at 10:07
  • You re welcome. Good luck to your project. – kboul Mar 29 '21 at 10:11
  • 1
    Thank you again, you've been a wonderful help – Ray Purchase Mar 29 '21 at 10:16
  • Just as a final resolution to this unfolding drama, we just needed to check the coords array is not empty before rendering the Bounds: {coords.length > 0 && } – Ray Purchase Mar 29 '21 at 14:31