Search code examples
reactjstypescriptmapboxmapbox-gl

What are the correct types to map variables using Mapbox in React?


After taking a look to mapbox documentation, I wrote this component

import React from "react";
import mapboxgl from "mapbox-gl";
import "./Map.css";

mapboxgl.accessToken = "...";

type Props = {
  longitude: number;
  latitude: number;
};

class Map extends React.Component<Props> {
  private mapContainer: any;
  private map: any;

  componentDidMount(): void {
    this.map = new mapboxgl.Map({
      container: this.mapContainer,
      center: [this.props.longitude, this.props.latitude],
      style: "....",
      zoom: 13
    });
  }
  render(): JSX.Element {
    return (
      <div
        ref={(el): void => {
          this.mapContainer = el;
        }}
        className="mapContainer"
      />
    );
  }
}

export default Map;

This code works correctly, however, in my real project, we are using TypeScript. I don't want to use any for mapContainer and map.

I have imported "@types/mapbox-gl": "^1.7.0", into my project, but I cannot figure out what type I have to use here.

My tries until now:

  • private map:mapboxgl.Map. It seems to be correct, but then it complais because it is not initialized on the constructor. According to mapbox documentation it has to be initialize on componentDidMount. I can type the variable with mapboxgl.Map | null. But then i have to check constally to not be null, to remove some annoying warnings.
  • private mapContainer = React.userRef<HtmlElement>(null);. But then, when i try to initialize the map, it says Type 'RefObject<HTMLElement>' is not assignable to type 'string | HTMLElement'.

Any ideas?


Solution

  • Based upon your code, the following works. First, you need to import the Map type which get's exported from mapbox-gl:

    import mapbox-gl, { Map } from "mapbox-gl";
    

    Which later can be used as a type for the map inside your component:

    private map: Map | undefined; // | undefined is needed here otherwise Typescript will complain about a missing initialiser inside the constructor.
    

    Now comes the tricky part, container is defined as:

    container: string | HTMLElement
    

    inside the MapboxOptions type definition. Therefore we have to workaround it a little bit:

    class SampleMap extends React.Component<Props> {
      private mapContainer: HTMLElement | null | undefined = undefined;
      private map: Map | undefined;
    
      componentDidMount(): void {
        this.map = new mapboxgl.Map({
          container:
            this.mapContainer === undefined || this.mapContainer === null
              ? "" // or pass in some other HTMLElement which is definitely defined or similar ...
              : this.mapContainer,
          ... // abbreviated
        });
      }
      render(): JSX.Element {
        ... // abbreviated
      }
    }
    

    There a check if the mapContainer is defined and not null and pass in the mapContainer otherwise a string or maybe you could pass in some other HTMLElement which you know is 100% defined. E.g. document#root.

    Complete code can be found here: on CodeSandbox