Build a React MapboxGL Component with Hooks

In a recent update, React brought forth the concept of hooks into its popular Javascript framework. React hooks are a series of methods and attributes which provide React stateless (or function) components the same abilities of React class components. They have become an instant success with the React developer community for their ability to make developers codebases more readable and compact without costing in performance.

Since this update came out, there have been many libraries that have been released which make use of React hooks:

When anyone releases a new concept into the wild, it often takes a long time to be widely adopted by the community. But in this case, the community bought the idea of React hooks in a short amount of time, making their own implementations of various components. But applied concepts such as web maps are a bit more tricky to implement. A map component that relies on a javascript doesn’t just need to display data. It needs to carry out side effects such as starting up the map, setting the proper position, and ensuring that it’s connected to the correct DOM element. This quick example shows you how you can overcome all these challenges so that you can display a Mapbox GL JS map in a functional component.

Building a Basic component

If you’re looking for something to copy, look no further. You can also find the finished version here: (https://codesandbox.io/s/mapboxgl-hook-map-uovgr).

import React, { useEffect, useRef, useState } from "react";
import mapboxgl from "mapbox-gl";
import "mapbox-gl/dist/mapbox-gl.css";

const styles = {
  width: "100vw",
  height: "calc(100vh - 80px)",
  position: "absolute"
};

const MapboxGLMap = () => {
  const [map, setMap] = useState(null);
  const mapContainer = useRef(null);

  useEffect(() => {
    mapboxgl.accessToken = process.env.REACT_APP_MAPBOX_KEY;
    const initializeMap = ({ setMap, mapContainer }) => {
      const map = new mapboxgl.Map({
        container: mapContainer.current,
        style: "mapbox://styles/mapbox/streets-v11", // stylesheet location
        center: [0, 0],
        zoom: 5
      });

      map.on("load", () => {
        setMap(map);
        map.resize();
      });
    };

    if (!map) initializeMap({ setMap, mapContainer });
  }, [map]);

  return <div ref={el => (mapContainer.current = el)} style={styles} />;
};

export default MapboxGLMap;

In this component, you can see that I’m making use of three of the hooks “useState”, “useRef”, and “useEffect”. Each of these hooks are useful in running the Map component, and without them, this component would be significantly less robust for a variety of tasks. Let’s go through them:

useState

The useState hook is a direct replacement for state in React class components. In its easiest form, declaring useState with an initial value returns an array that contains a getter and a setter. If your functional component has only one or two things that it’s changing, this is the hook you want to use. If you to manage more complex states, however, check out useReducer. This hook is much more effective at modifying changes of all sizes!

useEffect

The useEffect hook is best-explained as a tool that “lets you perform side effects in function components”. What’s a side effect?

Imagine that we want to load the map only on the initial component load. The action of generating the map for render can be considered a side-effect, but so is ensuring the we only wish to generate it a first time.

useRef

Finally, the last hook we use in this example, useRef, is a reference to something in the component which will always exist for the entire life of the component. In our case, we initialize our mapContainer to null, then on the first render we associate it with the mapContainer ref.

Going further

There’s a lot further we can go with this. For managing layers, or the position of the map, you should look into the “useReducer” hook, which provides your functional component the same techniques of a proper javascript reducer. This is more efficient then using the setState hook for managing a large object, or using multiple instances of setState for all the things. Remember to make liberal use of the “useEffect” hook for anything that results in effects that are secondary to rendering the data.

If you have any questions about building apps to help you display collection or provision geospatial knowledge, Sparkgeo is here to help you!