🌏

react-map-gl + Search JS (React)

2024/07/31に公開

はじめに

前回に続き、react-map-glを試します。今回は以下の記事でご紹介したReact実装のSearch JSを組み合わせてみます。

https://zenn.dev/ottylab/articles/ab629f6696e865

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

おさらい

Search JSのReact実装の使い方をおさらいしましょう。以下のようにpropsを指定して使用します。特にここで大事なのはmapです。ここにはMapbox GLJSのMapオブジェクトを指定します。

<SearchBox
  accessToken={accessToken}
  map={mapInstanceRef.current}
  mapboxgl={mapboxgl}
  value={inputValue}
  options={{ country: 'jp', language: 'ja' }}
  onChange={(d) => {
    setInputValue(d);
  }}
  marker
/>

Mapオブジェクトの取得方法

react-map-glではMapbox GL JSのMapオブジェクトはMapコンポーネント内部で作成されています。また、react-map-glのコンポーネントはコンテキスト経由でMapオブジェクトにアクセス可能です。しかし、外部には公開されていません。

そこで代わりにuseMapというフックが定義されています。React JSはreact-map-glのコンポーネントではないので、これを利用します。

動かないコード

ということで、以下のようにAppコンポーネントの中でuseMapを使用し、SearchBoxコンポーネントで使用してみました。が、このコードではSerchBox検索後のカメラの移動、ピンの設置等が動作しません。

function App() {
  const {current: map} = useMap();
  const [inputValue, setInputValue] = useState('');
  return (
    <Map
      mapLib={import('mapbox-gl')}
      initialViewState={{
        longitude: 139.76711,
        latitude: 35.68074,
        zoom: 3 
      }}
      style={{position: "absolute", top: 0, bottom: 0, width: "100%"}}
      mapStyle="mapbox://styles/mapbox/streets-v12"
      mapboxAccessToken={accessToken}
    >
      <SearchBox
        accessToken={accessToken}
        map={map?.getMap()}
        mapboxgl={mapboxgl}
        value={inputValue}
        options={{ country: 'jp', language: 'ja' }}
        onChange={(d) => {
          setInputValue(d);
        }}
        marker
      />
    </Map>
  )
}

これはuseContextの以下の制限によるものと考えられます。

useContext() call in a component is not affected by providers returned from the same component. The corresponding <Context.Provider> needs to be above the component doing the useContext() call.

ラップする

ということで、SearchBoxコンポーネントをラップするコンポーネントを作成します。

SearchBoxWrapper.jsx
import { useState } from 'react';
import { useMap }  from 'react-map-gl';
import { SearchBox } from '@mapbox/search-js-react';
import mapboxgl from 'mapbox-gl';

export default function SearchBoxWrapper(props) {
  const {current: map} = useMap();
  const [inputValue, setInputValue] = useState('');

  return (
      <SearchBox
        accessToken={props.accessToken}
        map={map.getMap()}
        mapboxgl={mapboxgl}
        value={inputValue}
        options={{ country: 'jp', language: 'ja' }}
        onChange={(d) => {
          setInputValue(d);
        }}
        marker
      />
  )
}

そして、以下のように使用します。

App.jsx
<Map
  mapLib={import('mapbox-gl')}
  initialViewState={{
    longitude: 139.76711,
    latitude: 35.68074,
    zoom: 3 
  }}
  style={{position: "absolute", top: 0, bottom: 0, width: "100%"}}
  mapStyle="mapbox://styles/mapbox/streets-v12"
  mapboxAccessToken={accessToken}
>
  <SearchBoxWrapper accessToken={accessToken} />
/Map>

まとめ

Search JSはreact-map-glのコンポーネントではないので一工夫必要でした。

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

Discussion