Mapbox FlyTo method using useMap hook
Asked Answered
D

3

5

I want to achive the fly animation to different locations on a click event using mapbox. I have read in the docs that useMap provide the flyTo method that could do exactly what i want, but i keep getting undefined Image of the error message

I am a little bit lost on how to use the flyTo method, or if it works with the way i have setup mapbox with react. I find very little information about this topic on the internett tho.

This is the code i use:

import MapBox, { Marker, Popup, useMap } from "react-map-gl";

const Map = () => {
  const { current: map } = useMap();

  const { loading, error, data } = useLoading(
    async () => await fetchJSON("APIURL")
  );
  console.log(data);
  const [showPopup, setShowPopup] = useState(false);
  const [changeViewport, setChangeViewport] = useState();
  const [viewport, setViewport] = useState({
    latitude: 59.913868,
    longitude: 10.752245,
    zoom: 4,
  });

  const handleLocations = (item) => {
    map.flyTo({ center: [83, 23] });
    console.log(item[1], item[0]);
  };

  if (!data) {
    return "";
  }
  if (error) {
    return <p>Error: {error.toString()}</p>;
  }

  return (
    <Container>
      <SubContainer>
        <div
          style={{
            width: "100%",
            padding: "0.5rem",
          }}
        >
          <div
            style={{
              display: "flex",
              padding: "1rem",
            }}
          >
            <img src={zeiptLogo} alt="company logo" />
            <Title>Enabled store locations</Title>
          </div>

          <div>
            <Button>Oslo</Button>
            <Button>Trondheim</Button>
            <Button>Bergen</Button>
            <Button>Vis alle</Button>
          </div>
          <Input>
            <input type="text" />
          </Input>
          <Scroll>
            {Object.keys(data)
              .filter((value) => data[value])
              .map((item) => {
                return (
                  <>
                    <StoreNames
                      onClick={() => handleLocations(data[item].coords)}
                    >
                      <h4>{data[item].store_name}</h4>
                      <MdKeyboardArrowRight size={20} />
                    </StoreNames>
                  </>
                );
              })}
          </Scroll>
        </div>
        <MapBox
          mapboxAccessToken={MAPBOX_TOKEN_KEY}
          initialViewState={viewport}
          style={{ width: "100%", height: "100%" }}
          mapStyle="mapbox://styles/mapbox/streets-v8"
          movi
        >
          {Object.keys(data)
            .filter((value) => data[value])
            .map((item) => {
              return (
                <>
                  <Marker
                    longitude={data[item].coords[0]}
                    latitude={data[item].coords[1]}
                    anchor="bottom"
                  >
                    <HiLocationMarker size={35} color="#00519a" />
                  </Marker>
                  {showPopup && (
                    <Popup
                      longitude={data[item].coords[0]}
                      latitude={data[item].coords[1]}
                      anchor="bottom"
                      onClose={() => setShowPopup(false)}
                    >
                      You are here
                    </Popup>
                  )}
                </>
              );
            })}
        </MapBox>
      </SubContainer>
    </Container>
  );
};

export default Map;
Damato answered 2/5, 2022 at 8:6 Comment(0)
M
6

The useMap hook is mostly useful when you want to access the map instance outside of the component that contains <MapBox />. In your case, just use react refs:

const mapRef = useRef(null)

and then attach the ref to the MapBox component:

   <MapBox
       ref={mapRef} <= attach ref here
       mapboxAccessToken={MAPBOX_TOKEN_KEY}
       initialViewState={viewport}
       style={{ width: "100%", height: "100%" }}
       mapStyle="mapbox://styles/mapbox/streets-v8"
       movi
    >

Now you can use the flyTo like this:

mapRef.current?.flyTo({ center: [83, 23] })
Metrorrhagia answered 28/2, 2023 at 9:53 Comment(0)
T
1
  const { current: map } = useMap();

map will always be undefined, as it is not in the context provider tree.

https://reactjs.org/docs/hooks-reference.html#usecontext

you can useMap only in the components wrapped by <MapBox/>

in order to use the map instance you can use one of the following callback functions: https://visgl.github.io/react-map-gl/docs/api-reference/map#callbacks to store the map instance using useRef

onLoad={(event) => {
mapRef.current = event.target
}}
Teammate answered 27/5, 2022 at 14:9 Comment(0)
T
0

Both answers from red baha and Radi Mortada are correct.

To use flyto in your MapContainer, you need to attach a ref (mapRef) to the Map component.

To use flyto from the useMap hook, it needs to be used within a child component of the Map component, as useMap uses the forwarded mapRef from the parent.

import MapBox as Map from "react-map-gl";

const MapContainer = () => {
    const mapRef = useRef(null)

    // You can use mapRef?.current?.flyto(...) here in MapContainer

    return <Map ref={mapRef} ... >
               <Child />
           </Map>
}

const Child = () => {
    const { current: map } = useMap()

    // You can use map?.flyto() here in Child.
    // But only if it's a child component of <Map />!
    // Otherwise map (= mapRef from parent) will be undefined.

    return ...
}
Trihedral answered 15/9, 2023 at 15:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.