Jonathan Lauwers
Jonathan Lauwers

Reputation: 285

Mapbox JS - Update map programatically when parent state updates

Gatsby project - trying to highlight a Mapbox route when state with desired route is updated.

Current implementation:

Since the mapbox map only seems to load properly in the useEffect hook, I don't see any other way of making the popup appear based on the parent's activeItem. If there are alternatives, please let me know!

Approaches I have tried:

Current code:

const EmbeddedMap = ({ updateList, activeItem }) => {
  const mapContainerRef = useRef(null)
  const [list, setList] = useState([])

  useEffect(() => {    
    const map = new mapboxgl.Map({
      container: mapContainerRef.current,
      accessToken: process.env.GATSBY_MAPBOX_TOKEN,
      style: "mapbox://styles/mapbox/...",
      center: [-90.836, 51.134],
      zoom: 6,
    })

    const popup = new mapboxgl.Popup()

    const getUniqueFeatures = (array, comparatorProperty) =>
      array.filter(
        (v, i, a) =>
          a.findIndex(
            t =>
              t.properties[comparatorProperty] ===
              v.properties[comparatorProperty]
          ) === i
      )

    const updateMap = map => {
      const features = map.queryRenderedFeatures({ layers: ["data"] })
      if (features) {
        const uniqueFeatures = getUniqueFeatures(features, "post_id")
        setList(uniqueFeatures)
      }
    }

    map.on("load", () => {
      updateMap(map)

      map.on("movestart", () => {
        map.setFilter("data", ["has", "title"])
      })

      map.on("moveend", () => {
        updateMap(map)
      })

      map.on("mousemove", "data", e => {
        map.getCanvas().style.cursor = "pointer"
        const feature = e.features[0]
        setPopup(map, popup, feature)
      })

      map.on("mouseleave", "data", () => {
        map.getCanvas().style.cursor = ""
      })

      map.on("click", "data", e => {
        const feature = e.features[0]
        map.flyTo({
          center: feature.geometry.coordinates[0],
        })
        setPopup(map, popup, feature)
      })
    })

    return () => map.remove()
  }, [])

  return (
    <div>
      <EmbeddedMapStyled ref={mapContainerRef} />
    </div>
  )
}

Upvotes: 0

Views: 526

Answers (1)

Jonathan Lauwers
Jonathan Lauwers

Reputation: 285

Changing my approach to a ref solved the problem:

const map = useRef(null)
map.current = new mapboxgl.Map({
      container: mapContainerRef.current,
      accessToken: process.env.GATSBY_MAPBOX_TOKEN,
      style: "mapbox://styles/data",
      center: [-114.836, 51.134],
      zoom: 6,
    })

Then I can easily reuse the map by using for example:

map.current.flyTo({
      center: activeItem.startPoint,
    })

Upvotes: 0

Related Questions