April 9, 2019

How to use mapbox-gl with React hooks

How to use mapbox-gl with React hooks

For a particular project I needed to instantiate the mapbox-gl.js library via a functional component instead of the usual way throughout a class component and a reference to the element you want to bind the map to.

One of the reasons to do this was to use only reacts new hooks and the context api for state management instead of adding a library such as redux. After googling without any success I managed it finally doing it the following way.

First I build a main component which is also our entry point to the app itself. Within this component I nested the Map component where I would instantiate Mapbox. In the parent component I use Reacts context provider to serve my store to its child components. To the Map component I would pass in a configuration object to its props including mapbox-gl itself after assigning the Mapbox token. The example below shows a simplified way of the main component.

const MapContainer = ({...props}) => {
    mapboxgl.accessToken = "your-token";
    
    const opts = {
        mapbox: mapboxgl,
        style: 'mapbox://styles/mapbox/streets-v9'
    }
    
    return (
      <Provider>
        <Map opts={opts} />
      </Provider>
    );
}

The Map component could now use the useEffect hook to create the Map instance and we could pass as second argument our reference to run useEffects only once the reference has been assigned. But we have now one issue, our useEffects hook runs before our component is rendered. To solve that issue we simply use the useLayoutEffect hook instead of the useEffect hook. This hook is basically the same with the only difference that it will run after our component has been rendered completely. We don't need now to pass in the reference as second argument, instead we pass an empty array which will make our hook run just once.

const Map = ({...props}) => {
    const mapContainer = React.createRef();
    const { mapbox, style } = props;
    
    useLayoutEffect(() => {
        new opts.mapbox.Map({
          style,
          container: mapContainer.current
        });
    }, []);
    
    return (
        <div
          style={{ width: "100vw", height: "100vh" }}
          className="map-container"
          ref={mapContainer}
        />
    );
}

Now we can easily use the rest of the react hooks and the context api to build our application without writing a single class component.

>>> Check out my Udemy courses.