ReactアプリでMapbox GL JSを使う
Reactは、ユーザーインターフェースの構築に使われるポピュラーなJavaScriptライブラリです。ReactはDOMを操作するため、同じくDOMを操作して状態を管理する他のライブラリ(Mapbox GL JSなど)と連携させると、混乱してしまうことがあります。
このチュートリアルでは Mapbox GL JS を使用して地図をレンダリングし、地図の中心点の座標とそのズームレベルを表示し、ユーザーが地図を操作したときに表示される地図を更新する React ウェブアプリの作成方法を学びます。このチュートリアルで説明されている基本を使用するとReact と Mapbox GL JS の両方を使用する、より複雑なアプリを作成できるようになります。このチュートリアルでは React HooksとClass Componentsのコードを紹介しています。
はじめに
- Mapboxアクセストークン あなたのMapboxアクセストークンはアカウントページで確認できます。
- Mapbox GL JS Mapbox GL JSは、ウェブマップの構築に使用されるJavaScriptライブラリです。
- テキストエディタ HTML、CSS、JavaScriptの記述には、お好みのテキストエディタをお使いください。
- Node.js and npm Reactアプリをローカルで実行するために必要なコマンドを実行するには、Node.js and npmをインストールしてください。
- Reactに精通していること このチュートリアルを完了するためにReactの使用経験は必要ありませんが、基本的なコンセプトとワークフローに精通している必要があります。
Reactのアプリ構造を設定する
まずは、use-mapbox-gl-js-with-react
という新しいフォルダを作成します。
use-mapbox-gl-js-with-react
フォルダの中に、新しいファイルを作成します。
package.json
- このファイルは、ReactとMapbox GL JSを含む、アプリが必要とするすべてのNodeパッケージを指定するために使用します。
use-mapbox-gl-js-with-react
フォルダの配下に、public
というフォルダを作成します。public`というフォルダの中に、1つのファイルを作成します。
index.html
- このHTMLファイルは、ユーザーが対話的な操作ができるようにレンダリングされたMapboxの地図を表示します。
use-mapbox-gl-js-with-react
フォルダの配下に、src
という名前の別のフォルダを作成します。このsrc
フォルダの中に、3つのファイルを作成します。
App.js
- このJavaScriptファイルはReactアプリをセットアップします。index.css
- このCSSファイルは、マップとサイドバーを正しくフォーマットするためのスタイルを含みます。index.js
- このJavaScriptファイルは、Reactアプリを設定します。index.js
: このJavaScriptファイルは、Mapboxのマップをブラウザにレンダリングします。
これらのフォルダとファイルを作成すると、以下のようなファイル構成になります。
以下のコードを package.json
にコピーしてください。
{"name": "use-mapbox-gl-js-with-react","version": "0.1.0","private": true,"dependencies": {"mapbox-gl": "^2.14.1","react": "^17.0.2","react-dom": "^17.0.2","react-scripts": "^4.0.3"},"scripts": {"start": "react-scripts start","build": "react-scripts build"},"eslintConfig": {"extends": ["react-app"]},"browserslist": ["defaults","not ie 11"],"author": "Mapbox","license": "MIT"}
このファイルには、React固有のNodeパッケージであるreact
とreact-dom
に加えて、アプリをビルドするためのCreate React Appとしても知られるreact-scripts
と、Mapbox GL JSにアクセスするためのmapbox-gl
が必要です。
変更を保存してください。
コマンドラインで、作成した use-mapbox-gl-js-with-react
フォルダに移動します。このフォルダの中で、npm install
というコマンドを実行すると、package.json
ファイルで指定したすべてのNodeパッケージがインストールされます。このステップでは、package-lock.json
ファイルも作成されます。
HTMLページの作成
public/index.html
ファイルを開き、以下のコードをコピーアンドペーストします。
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><metaname="description"content="Create a React web app that uses Mapbox GL JS to render a map"/><title>Use Mapbox GL JS in a React app</title></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
このコードは、ユーザーが目にするHTMLページの構造を作成します。ページの <body>
内には、ID root
を持つ <div>
要素があります。この<div>
は、Reactアプリがページ上でレンダリングされるためのコンテナです。
変更を保存してください。
Reactアプリの作成
src/index.js
ファイルを開きます。以下を追加して、2つのスタイルシートと、これから構築するMapbox GL JSマップをインポートします。
import React from 'react';import ReactDOM from 'react-dom';import 'mapbox-gl/dist/mapbox-gl.css';import './index.css';import App from './App'; ReactDOM.render(<React.StrictMode><App /></React.StrictMode>,document.getElementById('root'));
最初のスタイルシートには、地図を表示するためのMapbox GL JSのスタイルが含まれています。2つ目のスタイルシートは、先ほど作成したindex.css
ファイルで、ここにアプリ固有のCSSを追加していきます。
Mapbox GL JSの追加
src/App.js
ファイルを開きます。以下のimport文をファイルの先頭に追加します。
import React, { useRef, useEffect, useState } from 'react';
次に、Mapbox GLをインポートし、アクセストークンを追加します。Mapbox GLをCreate React Appで使用するには、exclude mapbox-gl from transpilationに感嘆符(!)を追加し、eslintルールimport/no-webpack-loader-syntax
を無効にする必要があります。mapboxgl
accessToken
プロパティ に Mapbox アクセストークンの値を設定します。
import mapboxgl from '!mapbox-gl'; // eslint-disable-line import/no-webpack-loader-syntaximport MapboxLanguage from '@mapbox/mapbox-gl-language'; // eslint-disable-line import/no-extraneous-dependencies
これでReactアプリの設定ができました! 次のいくつかのステップでコードを追加する構造を作るために、App.js
の下部に以下を追加します。
</div>
変更を保存してください。
アプリのデフォルト状態の設定
次に、地図の初期の緯度、経度、ズームに使用するアプリのデフォルト値をいくつか設定します。App
の中に以下を追加します。
export default function App() {const mapContainer = useRef(null);const map = useRef(null);const [lng, setLng] = useState(139.4534);const [lat, setLat] = useState(35.4548);
この state
には、マップの経度、緯度、ズームが格納されます。これらの値はすべて、ユーザーがマップを操作することによって変化します。
次に、マップを初期化します。以下のコードは、HTMLページのDOMツリーにアプリが挿入された直後に呼び出されます。
useEffect(() => {if (map.current) return; // initialize map only oncemap.current = new mapboxgl.Map({container: mapContainer.current,style: 'mapbox://styles/mapbox/streets-v11',center: [lng, lat],zoom: zoom});
Mapboxの地図は、ReactのEffect hookや、クラスを使用している場合はcomponentDidMount()
のライフサイクルメソッド内で初期化されます。ここで地図を初期化すると、Reactが地図を含む要素を作成する前に、Mapbox GL JSによる地図のレンダリングを開始しないようになります。また、地図の初期化の中で以下のオプションを設定します。
container
オプションは、Mapbox GL JSに特定のDOM要素の中で地図をレンダリングするように指定します。ここでアプリがmapContainer
useRef
または、クラスコンポーネントを使用している場合はref
を受け取ることを想定しています。このチュートリアルの後半では、マップコンテナとして機能する HTML 要素にref
を割り当てます。style
オプションは、地図が使用するスタイルを定義します (mapbox://styles/mapbox/streets-v11
)。center
とzoom
オプションは、ステートに格納されているlng
,lat
,zoom
の値を使って、地図の中心座標とズームレベルを設定します。- フックを使用している場合は、地図の初期化状態を保存するために、
map
のuseRef
も作成しました。このref
は、ユーザーが地図を操作したときに、地図が再ロードされないようにします。
変更を保存してください。
地図のレンダリング
次に、アプリ内で地図をレンダリングする必要があります。ReactアプリでMapboxの地図を初期化するためのエントリーポイントは、return
文で提供される要素のひとつです。以下のコードをアプリの App
の閉じた中括弧の上に追加してください。
setZoom(map.current.getZoom().toFixed(2));});<div><div className="sidebar">Longitude: {lng} | Latitude: {lat} | Zoom: {zoom}
mapContainer
の ref
は、App
を新しい <div>
要素で HTML ページに描画するよう指定しています。
マップが正しく表示されるためには、いくつかのスタイリングルールが必要です。以下のコードを index.css
ファイルに追加してください。
.map-container {height: 400px;}
変更内容を保存します。コマンドラインで、npm start
というコマンドを実行します。これにより、ローカルサーバーが起動し、新しいMapboxマップを含む新しいページがブラウザで開かれます。
ブラウザの開発者コンソールを開くと、「no-unused-vars」という警告が表示される場合があります。これらの変数は次のステップで使用しますのでご安心ください。
新しい座標の保存
次に、ユーザーが地図を操作したときに得られる新しい緯度、経度、ズームを保存する関数を作成する必要があります。Mapbox GL JS map.on('move')
関数を記述して、ユーザーが地図を操作したときに、ステートをこれらの新しい値に設定します。フックを使用している場合は、追加の useEffect
を作成し、以下のコードを追加します。クラスを使用している場合は、componentDidMount()
の中に以下のコードを追加します。
const language = new MapboxLanguage();map.addControl(language);}); useEffect(() => {if (!map.current) return; // wait for map to initializemap.current.on('move', () => {setLng(map.current.getCenter().lng.toFixed(4));
この関数は、フックを使用している場合は useState()
、クラスを使用している場合は setState()
を使用して、マップが移動したときに lng
, lat
, zoom
の値をリセットします。また、以下のメソッドも使用しています。
getCenter()
- Mapbox GLのJSメソッドで、地図の中心にあるポイントの新しい経度と緯度を取得します。getZoom()
- Mapbox GL JS メソッド, 地図に設定されているズームレベルを決定します。toFixed()
- JavaScriptのメソッドで、結果として得られる浮動小数点数を指定された桁数に切り上げるためのものです。
座標の表示
この情報を収集して保存することができたので、return
を使って地図上に表示することができます。地図を格納するために作成した <div>
の開始タグの中に、地図の経度、緯度、ズームを表示するための新しい <div>
を追加します。これでreturn
文は次のようになります。
setZoom(map.current.getZoom().toFixed(2));});}); return (<div><div className="sidebar">Longitude: {lng} | Latitude: {lat} | Zoom: {zoom}
サイドバーがページに正しく表示されるためには、いくつかのスタイリングルールが必要です。以下のCSSを index.css
ファイルに追加してください。
.sidebar {background-color: rgba(35, 55, 75, 0.9);color: #fff;padding: 6px 12px;font-family: monospace;z-index: 1;position: absolute;top: 0;left: 0;margin: 12px;border-radius: 4px;}
作業内容を保存して、ブラウザのページに戻ります。現在、地図の左上隅にはサイドバーがあり、index.css
で設定したCSSルールに従ってスタイルが設定されています。このサイドバーには、地図の中心の現在の緯度・経度と、ズームレベルが表示されます。これで、地図をズームしたりパンしたりすると、サイドバーの内容が更新されます。
完成プロダクト
Mapbox GL JSを使用して、地図をレンダリングし、地図の中心座標とズームレベルを表示し、ユーザーが地図を操作したときにその表示を更新するReactアプリを作成しました。
最終的な index.html
ページは以下のようになります。
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><metaname="description"content="Create a React web app that uses Mapbox GL JS to render a map"/><title>Use Mapbox GL JS in a React app</title></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
最終的な index.js
ファイルは以下のようになります。
import React from 'react';import ReactDOM from 'react-dom';import 'mapbox-gl/dist/mapbox-gl.css';import './index.css';import App from './App'; ReactDOM.render(<React.StrictMode><App /></React.StrictMode>,document.getElementById('root'));
最終的な App.js
ファイルは以下のようになります。
import React, { useRef, useEffect, useState } from 'react';import mapboxgl from '!mapbox-gl'; // eslint-disable-line import/no-webpack-loader-syntaximport MapboxLanguage from '@mapbox/mapbox-gl-language'; // eslint-disable-line import/no-extraneous-dependencies mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN'; export default function App() {const mapContainer = useRef(null);const map = useRef(null);const [lng, setLng] = useState(139.4534);const [lat, setLat] = useState(35.4548);const [zoom, setZoom] = useState(9); useEffect(() => {if (map.current) return; // initialize map only oncemap.current = new mapboxgl.Map({container: mapContainer.current,style: 'mapbox://styles/mapbox/streets-v11',center: [lng, lat],zoom: zoom}); const language = new MapboxLanguage();map.addControl(language);}); useEffect(() => {if (!map.current) return; // wait for map to initializemap.current.on('move', () => {setLng(map.current.getCenter().lng.toFixed(4));setLat(map.current.getCenter().lat.toFixed(4));setZoom(map.current.getZoom().toFixed(2));});}); return (<div><div className="sidebar">Longitude: {lng} | Latitude: {lat} | Zoom: {zoom}</div><div ref={mapContainer} className="map-container" /></div>);}
最終的な index.css
ファイルは以下のようになります。
.map-container {height: 400px;} .sidebar {background-color: rgba(35, 55, 75, 0.9);color: #fff;padding: 6px 12px;font-family: monospace;z-index: 1;position: absolute;top: 0;left: 0;margin: 12px;border-radius: 4px;}
次のステップ
ここではMapbox GL JSを使用したReactアプリを作成しました。Mapbox React examples GitHub repositoryでより複雑なサンプルを参照することができます。