🔎

Mapbox Search JS を触ってみる (SearchBox/React編)

2024/06/13に公開

はじめに

この記事は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と同じものを作成します。

以下が本サンプルのデモです。SafariFirefoxを使用されている方はデモが実行されない可能性があります。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#addControlMapboxSearchBoxを追加しますが、Reactではその手法が使えません。そこで、自分で表示する場所を準備します。

<div
  id="control"
>
  // SearchBox
</div>

ドキュメントの通り、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のときと同じです。#controlSearchBoxのコンテナです。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 のカスタマイズ

カスタマイズを試してみます。

テーマの変更

以下のようにSearchBoxthemepropを記述することで検索ボックス部分のテーマをカスタマイズできます。記述内容はWebと同じです。variables定義済みのテーマを変更できます。ここではテキストの文字色と背景色を変更しています。cssTextは定義済み以外のスタイルを適用したい場合に直接CSSとして記述する文字列を指定します。ここではマウスカーソルが重なったタイミングで背景色を変更しています。

theme={{
  variables: {
    colorText: "red",
    colorBackground: "gold"
  },
  cssText: "input:hover { background: lightyellow; }"
}}

マーカーの変更

SearchBoxmarkerpropを変更することでマーカーをカスタマイズできます。Mapbox GL JSのMarkerインスタンス作成時のオプションが指定できます。

以下のMapbox GL JSのサンプルコードで作成されているマーカーを実装してみましょう。
https://docs.mapbox.com/mapbox-gl-js/example/marker-horizon-aligned/

まず、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],
}}

結果

結果は以下のとおりです。SafariFirefoxを使用されている方はデモが実行されない可能性があります。Chromeで表示するか、 https://stackblitz.com/edit/vitejs-vite-bcbhaq を直接ご参照ください。

まとめ

少しクセがありますが、React版でも簡単にSearch Boxの機能が使えることがわかりました。

GitHubで編集を提案
マップボックス・ジャパン合同会社

Discussion