react-map-gl + Search JS (React)
はじめに
前回に続き、react-map-glを試します。今回は以下の記事でご紹介したReact実装のSearch JSを組み合わせてみます。
以下がデモです。SafariやFirefoxを使用されている方はデモが実行されない可能性があります。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コンポーネントをラップするコンポーネントを作成します。
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
/>
)
}
そして、以下のように使用します。
<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のコンポーネントではないので一工夫必要でした。
Discussion