Next.jsで関数コンポーネントを使ってMapboxの地図を表示する

以前から、Next.js地図(Mapbox)を使って何かできないか?というのを試していました。2020年の3月くらいに、クラスコンポーネントを使って、どうにか地図が表示できたので一旦このブログに顛末を纏めたのが↓です。

Next.jsでMapboxを使って地図を表示する | shimar's blog

それから、半年以上経て、再度トライしたところ、なんとか関数コンポーネントでMapboxの地図が表示できたので、未来の自分のために纏めておこうと思います。

Mapboxの準備

ここは、特に難しいことはないので、前回同様割愛します。 Mapbox上での作業としては、アカウントを作って、アクセストークンを払い出すだけです。

一方、Next.jsのアプリケーション側の作業としては、package.jsonに以下のパッケージを追加すれば使えるようになります。

@types/mapbox-glは、TypeScriptでMapboxを使う場合に必要になります。

Mapコンポーネント

実際に作ったコンポーネント、今回は関数コンポーネントとして書いてみたのですが、おおよそ↓のようなコードになりました。

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

mapbox.accessToken = process.env.MAPBOX_TOKEN || "";

interface MapProps {
  center: [number, number];
  zoom: number | 5;
}

const Map = ({ center, zoom }: MapProps) => {
  const [mounted, setMounted] = useState<boolean>(false);
  const [map, setMap] = useState<mapbox.Map | null>(null);
  const styles: React.CSSProperties = {
    height: "100%",
    width: "100%",
  };

  const options: mapbox.MapboxOptions = {
    container: "map",
    style: "mapbox://styles/mapbox/streets-v11",
    center: center,
    zoom: zoom,
  };

  useEffect(() => {
    setMounted(true);
    setMap(new mapbox.Map(options));
  }, [mounted]);

  useEffect(() => {
    if (mounted && map) {
      map.setCenter(center);
    }
  }, [center]);

  return <div id="map" style={styles}></div>;
};

export default Map;

このMapコンポーネントを使う側(例えばPageなど)からは、

  • 地図の中心の座標
  • zoomレベル

を指定してもらう、という形にしました。

初めてこのコンポーネントを使って地図を描画する場合に、親のDOMが出来上がっていないために描画できなくなる問題を解消するために、このコンポーネントの状態として、

  • mounted
  • map

を管理するようにしました。

mountedは、DOMがレンダリングされたか否かを管理し、mapはMapboxの地図オブジェクトを管理します。これらを使って、コンポーネントがレンダリングされたらMapboxの地図オブジェクトを生成して、状態として格納する、というやり方にしました。これは、参考にさせて頂いたサイトを大いに真似しました。 それが、↓の部分です。

  const [mounted, setMounted] = useState<boolean>(false);
  const [map, setMap] = useState<mapbox.Map | null>(null);

  useEffect(() => {
    setMounted(true);
    setMap(new mapbox.Map(options));
  }, [mounted]);

それと、一度描画した地図に対して、ページ内で発生したイベントに応じて、再度中心座標をもらって、地図を更新するということをしたかったので、

useEffect(() => {
  if (mounted && map) {
    map.setCenter(center);
  }
});

で実現しました。

おわりに

前回はよくわからずできなかった、関数コンポーネントでMapboxの地図を使う方法がわかったので良かったです。 また、知見が得られたら記事にしようと思います。

他に良いやり方や、ツッコミがありましたらご指摘頂けると嬉しいです。

参考