React アプリで Mapbox GL JS を使用する
React は、ユーザーインターフェースを構築するためによく使用される JavaScript ライブラリです。React は DOM を操作するため、DOM を操作し状態を管理する他のライブラリ(例: Mapbox GL JS)と接続するのが混乱しがちです。
このチュートリアルでは、Mapbox GL JS を使用して地図をレンダリングし、地図の中心点の座標とズームレベルを表示し、ユーザーが地図と対話すると表示を更新する React Web アプリを作成する方法を学びます。このチュートリアルで説明する原則を使用して、React と Mapbox GL JS の両方を使用するより複雑なアプリを作成することができます。 このチュートリアルでは、React の Hooks と Class Components のコードを示します。
はじめに
- Mapbox アクセストークン。 Mapbox アクセストークンは アカウントページ にあります。
- Mapbox GL JS。 Mapbox GL JS は、Web 地図を作成するために使用される JavaScript ライブラリです。
- テキストエディタ。 HTML、CSS、JavaScript を記述するためにテキストエディタを使用します。
- Node.js と npm。 React アプリをローカルで実行するために必要なコマンドを実行するには、Node.js と 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 ファイルは、ブラウザに Mapbox 地図をレンダリングします。
これらのフォルダとファイルを作成すると、次のようなファイル構造になります:
use-mapbox-gl-js-with-react
package.json
public
index.html
src
App.js
index.css
index.js
次のコードを package.json
にコピーします:
{
"name": "use-mapbox-gl-js-with-react",
"version": "0.1.0",
"private": true,
"dependencies": {
"mapbox-gl": "^3.3.0",
"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
以外にも、アプリをビルドするために使用される react-scripts
(Create React App とも呼ばれます)と、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" />
<meta
name="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>
には root
ID を持つ <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 React, { useRef, useEffect, useState } from 'react';
import React from 'react';
次に、Mapbox GL をインポートし、アクセストークンを追加します。Create React App を使用するには、Mapbox GL をトランスパイルから除外するための感嘆符を追加し、 eslint ルール import/no-webpack-loader-syntax
を無効にする必要があります。accessToken
プロパティ を Mapbox アクセストークンの値に設定します:
import mapboxgl from '!mapbox-gl'; // eslint-disable-line import/no-webpack-loader-syntax
mapboxgl.accessToken = 'MAPBOX_ACCESS_TOKEN';
import mapboxgl from '!mapbox-gl'; // eslint-disable-line import/no-webpack-loader-syntax
mapboxgl.accessToken = 'MAPBOX_ACCESS_TOKEN';
React アプリをセットアップできます!次のコードを App.js
の下部に追加して、その後の手順で使用する構造を作成します:
export default function App() {
}
export default class App extends React.PureComponent {
}
変更を保存します。
アプリのデフォルト状態を設 定する
次に、アプリが初期の緯度、経度、ズームレベルを使用するデフォルト値を作成します。 App
の内部に次のコードを追加します:
const mapContainer = useRef(null);
const map = useRef(null);
const [lng, setLng] = useState(-70.9);
const [lat, setLat] = useState(42.35);
const [zoom, setZoom] = useState(9);
constructor(props) {
super(props);
this.state = {
lng: -70.9,
lat: 42.35,
zoom: 9
};
this.mapContainer = React.createRef();
}
状態は地図の経度、緯度、ズームレベルを保存します。これらの値はユーザーが地図 と対話するたびに変更されます。
次に、地図を初期化します。このコードは、アプリが HTML ページの DOM ツリーに挿入された直後に呼び出されます。
useEffect(() => {
if (map.current) return; // initialize map only once
map.current = new mapboxgl.Map({
container: mapContainer.current,
style: 'mapbox://styles/mapbox/streets-v12',
center: [lng, lat],
zoom: zoom
});
});
componentDidMount() {
const { lng, lat, zoom } = this.state;
const map = new mapboxgl.Map({
container: this.mapContainer.current,
style: 'mapbox://styles/mapbox/streets-v12',
center: [lng, lat],
zoom: zoom
});
}
Mapbox 地図は、React Effect フック またはクラスを使用している場合の componentDidMount()
ライフサイクル メソッド内で初期化されます。ここで地図を初期化すると、React が地図を含む要素を作成する前に Mapbox GL JS が地図をレンダリングしようとすることがないようにします。地図の初期化の中で次のオプションも設定します:
container
オプションは、Mapbox GL JS に特定の DOM 要素内に地図をレンダリングするよう指示します。ここで、アプリはmapContainer
useRef
またはref
を想定しています。クラスコンポーネントを使用している場合は後で HTML 要素にref
を割り当て、その要素が地図コンテナとして機能します。style
オプションは地図が使用するスタイルを定義します (mapbox://styles/mapbox/streets-v12
)。center
およびzoom
オプションは、状態に保存されているlng
、lat
、zoom
の値を使用して地図の中心座標とズームレベルを設定します。- フックを使用している場合は、
map
useRef
を作成して地図を初期化します。ref
はユーザーが地図を操作したときに地図が再ロードされないようにします。
変更を保存します。
地図をレンダリングする
次に、アプリで地図をレンダリングする必要があります。React アプリで Mapbox 地図を初期化するエントリポイントは、return
ステートメントで提供される単一の要素です。次のコードを App
の閉じ中括弧の上に追加します:
return (
<div>
<div ref={mapContainer} className="map-container" />
</div>
);
render() {
return (
<div>
<div ref={this.mapContainer} className="map-container" />
</div>
);
}
mapContainer
ref
は、App
を新しい <div>
要素にレンダリングすべきということを示します。
地図が正しくレンダリングされるためには、いくつかのスタイルルールが必要です。次のコードを index.css
ファイルに追加します:
変更を保存します。コマンドラインで npm start
コマンドを実行します。これによりローカルサーバーが起動し、新しい Mapbox 地図が含まれたページがブラウザに表示されます。
ブラウザの開発者コンソールを開くと、no-unused-vars
警告が表示される場合があります。心配ありません これは次のステップでこれらの変数を使用するためです。
新しい座標を保存する
次に、ユーザーが地図と対話した際に取得した新しい緯度、経度、ズームレベルを保存する関数を作成する必要があります。ユーザーが地図を移動したときに状態をこれらの新しい値に設定する Mapbox GL JS の map.on('move')
関数を作成します。フックを使用している場合は、以前の useEffect
を使用し、次のコードを地図の初期化の下に追加します。クラスを使用している場合は、次のコードを componentDidMount()
内に追加します:
map.current.on('move', () => {
setLng(map.current.getCenter().lng.toFixed(4));
setLat(map.current.getCenter().lat.toFixed(4));
setZoom(map.current.getZoom().toFixed(2));
});
map.on('move', () => {
this.setState({
lng: map.getCenter().lng.toFixed(4),
lat: map.getCenter().lat.toFixed(4),
zoom: map.getZoom().toFixed(2)
});
});
この関数はフックを使用している場合は useState()
、クラスを使用している場合はsetState()
を使用して、地図が移動した際に lng
、lat
、zoom
の値をリセットします。 また、次のメソッドを使用します。
getCenter()
は Mapbox GL JS メソッドで、地図の中心にあるポイントの新しい経度と緯度を取得します。getZoom()
は地図の現在のズームレベルを判断するための Mapbox GL JS メソッドです。toFixed()
は JavaScript メソッドで、結果の浮動小数点数を指定した桁数に丸めます。
座標を表示する
これで、この情報を収集して保存できるようになったため、return
を使用して地図に表示できます。作成した地図を保持する <div>
の開始タグの内部に、新しい <div>
を追加して地図の経度、緯度、およびズームレベルを表示します。return
ステートメントは次のようになります:
return (
<div>
<div className="sidebar">
Longitude: {lng} | Latitude: {lat} | Zoom: {zoom}
</div>
<div ref={mapContainer} className="map-container" />
</div>
);
render() {
const { lng, lat, zoom } = this.state;
return (
<div>
<div className="sidebar">
Longitude: {lng} | Latitude: {lat} | Zoom: {zoom}
</div>
<div ref={this.mapContainer} className="map-container" />
</div>
);
}
サイドバーを正しくページに表示するには、いくつかのスタイルルールが必要です。次のCSSをindex.css
ファイルに追加してください。
変更を保存し、ブラウザページに戻ります。今では上部左隅にサイドバーがあり、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" />
<meta
name="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-syntax
mapboxgl.accessToken = 'MAPBOX_ACCESS_TOKEN';
export default function App() {
const mapContainer = useRef(null);
const map = useRef(null);
const [lng, setLng] = useState(-70.9);
const [lat, setLat] = useState(42.35);
const [zoom, setZoom] = useState(9);
useEffect(() => {
if (map.current) return; // initialize map only once
map.current = new mapboxgl.Map({
container: mapContainer.current,
style: 'mapbox://styles/mapbox/streets-v12',
center: [lng, lat],
zoom: zoom
});
map.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>
);
}
import React from 'react';
import mapboxgl from '!mapbox-gl'; // eslint-disable-line import/no-webpack-loader-syntax
mapboxgl.accessToken = 'MAPBOX_ACCESS_TOKEN';
export default class App extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
lng: -70.9,
lat: 42.35,
zoom: 9
};
this.mapContainer = React.createRef();
}
componentDidMount() {
const { lng, lat, zoom } = this.state;
const map = new mapboxgl.Map({
container: this.mapContainer.current,
style: 'mapbox://styles/mapbox/streets-v12',
center: [lng, lat],
zoom: zoom
});
map.on('move', () => {
this.setState({
lng: map.getCenter().lng.toFixed(4),
lat: map.getCenter().lat.toFixed(4),
zoom: map.getZoom().toFixed(2)
});
});
}
render() {
const { lng, lat, zoom } = this.state;
return (
<div>
<div className="sidebar">
Longitude: {lng} | Latitude: {lat} | Zoom: {zoom}
</div>
<div ref={this.mapContainer} className="map-container" />
</div>
);
}
}
最終的な index.css
ファイルは次のようになります:
.map-container {
height: 400px;
}
.sidebar {
background-color: rgb(35 55 75 / 90%);
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 リポジトリで他のより複雑な例を探索できます。