Mapbox Search JS を触ってみる (SearchBox/React編)
はじめに
この記事はMapbox Search JS を触ってみる (SearchBox/Web編)の続きで、Search JSのSearchBox/Reactの使い方を見ていきます。Search Box - React QuickstartのIntegration with a Mapbox GL JS Mapを参考にしつつ、Add Search Box for Japanと同じものを作成します。
以下が本サンプルのデモです。SafariやFirefoxを使用されている方はデモが実行されない可能性があります。Chromeで表示するか、 https://stackblitz.com/edit/vitejs-vite-xlu7yg を直接ご参照ください。
プロジェクトの作成と依存関係のインストール
この記事ではvite + Reactを使用します。公式ガイドにしたがってプロジェクトを作成します。
% npm create vite@latest
Need to install the following packages:
create-vite@5.2.3
Ok to proceed? (y) y
✔ Project name: … searchjs-searchbox-react
✔ Select a framework: › React
✔ Select a variant: › JavaScript
Scaffolding project in /Users/yochi/Downloads/20240608/searchjs-searchbox-react...
Done. Now run:
cd searchjs-searchbox-react
npm install
npm run dev
次にSearch Box - React Quickstartにしたがい、serch-js-react
をインストールします。
% npm install @mapbox/search-js-react
最後にMapbox GL JSをインストールします。
% npm install mapbox-gl
コードの記述
それでは実際にコードの中身を見ていきます。
App.js
デフォルトで作成されていた雛形を削除し、以下の内容を記載します。
import { useRef, useEffect, useState } from 'react';
import { SearchBox } from '@mapbox/search-js-react';
import mapboxgl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
import './App.css'
const accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';
export default function App() {
const mapContainerRef = useRef();
const searchBoxRef = useRef();
const mapInstanceRef = useRef();
const [mapLoaded, setMapLoaded] = useState(false);
const [inputValue, setInputValue] = useState('');
useEffect(() => {
mapboxgl.accessToken = accessToken;
mapInstanceRef.current = new mapboxgl.Map({
container: mapContainerRef.current,
style: 'mapbox://styles/mapbox-search-web/cl5l944i6000k14o4ing22srv',
center: [139.77, 35.68],
zoom: 6,
});
mapInstanceRef.current.on('load', () => {
setMapLoaded(true);
});
}, []);
return (
<>
<div
id="map-container"
ref={mapContainerRef}
></div>
<div
id="control"
>
<SearchBox
accessToken={accessToken}
map={mapInstanceRef.current}
mapboxgl={mapboxgl}
value={inputValue}
options={{ country: 'jp', language: 'ja' }}
onChange={(d) => {
setInputValue(d);
}}
marker
/>
</div>
</>
);
}
内容については以下のとおりです。
Mapの作成
Mapbox GL JSはReactに対応していないため、userEffect
の中で作成します。
useEffect(() => {
// 中身
}, []);
中身を見ていきます。
まず、Mapオブジェクトを作成します。これはよく見るといつも通りです。このコードではcontainer
としてref
(DOMオブジェクト)を指定していますが、いつも通りid
名 (ここではmap-container
)でもOKです。これは、Mapクラスのコンストラクタがどちらのケースでも処理するように記述されているからです。
mapInstanceRef.current = new mapboxgl.Map({
container: mapContainerRef.current,
style: 'mapbox://styles/mapbox-search-web/cl5l944i6000k14o4ing22srv',
center: [139.77, 35.68],
zoom: 6,
});
ロード後の処理
このサンプルでは使用しませんが、load
時の処理を記述しています。mapLoaded
ステートをtrue
に変更しています。このステートを監視して、各種UIの変更処理を記述します。
mapInstanceRef.current.on('load', () => {
setMapLoaded(true);
});
JSX
次にJSXを見ていきます。
return (
<>
// 中身
</>
);
Mapコンテナ
Mapの作成で出てきたMapのコンテナです。
<div
id="map-container"
ref={mapContainerRef}
></div>
Controlコンテナ
SearchBox
のコンテナです。WebではMap#addControl
でMapboxSearchBox
を追加しますが、Reactではその手法が使えません。そこで、自分で表示する場所を準備します。
<div
id="control"
>
// SearchBox
</div>
SearchBox
ドキュメントの通り、SearchBoxを作成します。指定可能なPropsはここに記載されています。
<SearchBox
accessToken={accessToken}
map={mapInstanceRef.current}
mapboxgl={mapboxgl}
value={inputValue}
options={{ country: 'jp', language: 'ja' }}
onChange={(d) => {
setInputValue(d);
}}
marker
/>
App.css
デフォルトで作成されていた雛形を削除し、以下の内容を記載します。#map-container
はMapのコンテナのスタイルです。これはWebのときと同じです。#control
はSearchBox
のコンテナです。addControl
を使ったときと同じ見た目に寄せています。
#map-container {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
#control {
margin: 10px 10px 0px 0px;
position: absolute;
top: 0;
right: 0;
display: block;
width: 300px;
}
index.css
デフォルトで作成されていた雛形を削除し、以下の内容を記載します。 Webのときと同じ設定を記述しています。
body {
margin: 0;
padding: 0;
}
Search Box/Web のカスタマイズ
カスタマイズを試してみます。
テーマの変更
以下のようにSearchBox
のtheme
propを記述することで検索ボックス部分のテーマをカスタマイズできます。記述内容はWebと同じです。variables
は定義済みのテーマを変更できます。ここではテキストの文字色と背景色を変更しています。cssText
は定義済み以外のスタイルを適用したい場合に直接CSSとして記述する文字列を指定します。ここではマウスカーソルが重なったタイミングで背景色を変更しています。
theme={{
variables: {
colorText: "red",
colorBackground: "gold"
},
cssText: "input:hover { background: lightyellow; }"
}}
マーカーの変更
SearchBox
のmarker
propを変更することでマーカーをカスタマイズできます。Mapbox GL JSのMarker
のインスタンス作成時のオプションが指定できます。
以下のMapbox GL JSのサンプルコードで作成されているマーカーを実装してみましょう。
まず、App.cssに以下のスタイルを追加します。また、ピンの画像は https://docs.mapbox.com/mapbox-gl-js/assets/pin.svg からダウンロードしてpublic
ディレクトリに入れておきます。
.marker {
background-image: url('/pin.svg');
background-size: cover;
cursor: pointer;
}
次に、App.jsxのApp()メソッド内で以下の定数を定義します。
const size = 100;
最後に、SearchBoxのpropsのmarker
を以下のように変更します。
marker={{
element: (() => {
const el = document.createElement('div');
el.className = 'marker';
el.style.width = `${size}px`;
el.style.height = `${size}px`;
return el;
})(),
rotationAlignment: "horizon",
offset: [0, -size / 2],
}}
結果
結果は以下のとおりです。SafariやFirefoxを使用されている方はデモが実行されない可能性があります。Chromeで表示するか、 https://stackblitz.com/edit/vitejs-vite-bcbhaq を直接ご参照ください。
まとめ
少しクセがありますが、React版でも簡単にSearch Boxの機能が使えることがわかりました。
Discussion