ReactアプリにGoogle Map入れてみた
ReactアプリにGoogleMap入れようと思って、結構詰まったのでメモメモ
やりたいことは↓のような感じで、マップとそこにマーカーを表示すること。
準備
ライブラリ
公式で@googlemaps/react-wrapper
というライブラリが提供されているのでそれを使います。
TypeScriptの人は型定義も必要です。
npm install @googlemaps/react-wrapper @types/google.mapstypes/google.maps
Google Cloud Platform
Google Cloud PlatformでAPIキーを取得する必要があります。
今回やろうとしてることは無料枠で収まります。
(気が向いたらこの手順もまとめます...)
Reactアプリで地図を表示する
基本は上記のチュートリアル通りに進めます。
チュートリアルはform要素でマップを操作するもので、
今回はformを使わないので不要なところがちょこちょこあります。
onClickとかonIdleとかいらないです。
↓できたソースコード↓
const render = (status: Status) => {
return <h1>{status}</h1>
}
function App() {
const position = {
lat: 43.035718552968376,
lng: 141.46244422700855
} as google.maps.LatLngLiteral;
return (
<div className='flex flex-row w-1/2 justify-center'>
<Wrapper apiKey='api_key' render={render}>
<Map style={{ width: '100%', aspectRatio: '16 / 9' }} center={position}/>
</Wrapper>
</div>
)
}
export default App
type MapProps = google.maps.MapOptions & {
style: { [key: string]: string };
children?: React.ReactElement<google.maps.MarkerOptions>[] | React.ReactElement<google.maps.MarkerOptions>;
}
export const Map: React.FC<MapProps> = ({ children, style, ...options }) => {
const ref = useRef<HTMLDivElement>(null);
const [map, setMap] = useState<google.maps.Map>();
useEffect(() => {
if (ref.current && !map) {
const option = {
center: options.center,
zoom: 16
};
setMap(new window.google.maps.Map(ref.current, option));
}
}, [ref, map]);
return (
<div ref={ref} style={style} />
);
}
childrenの型はReact.ReactElement<google.maps.MarkerOptions>[] | React.ReactElement<google.maps.MarkerOptions>
です。
ここがあってないと後々怒られます。
地図にマーカーを表示させる
ここだよっていう赤いやつを表示させたい。
チュートリアル通りにマーカーコンポーネントを作ります。
export const Marker: React.FC<google.maps.MarkerOptions> = (options) => {
const [markar, setMarkar] = useState<google.maps.Marker>();
useEffect(() => {
if (!markar) {
setMarkar(new google.maps.Marker());
}
return () => {
if (markar) {
markar.setMap(null);
}
};
}, [markar]);
useEffect(() => {
if (markar) {
markar.setOptions(options);
}
}, [markar, options]);
return null;
}
App.tsxにマーカーコンポーネントを追加します。
return (
<div className='flex flex-row w-1/2 justify-center'>
<Wrapper apiKey='AIzaSyCxI9yUwqIhyjEaElDOCS4eaQzVxFbJ6Tc' render={render}>
<Map style={{ width: '100%', aspectRatio: '16 / 9' }} center={position}>
+ <Marker position={position} />
</Map>
</Wrapper>
</div>
)
マップコンポーネントも修正します。
子コンポーネントとしてマーカーを受け取って表示する。
return (
<>
<div ref={ref} style={style} />
+ {
+ Children.map(children, (child) => {
+ if (isValidElement(child)) {
+ return cloneElement(child, { map });
+ }
+ })
+ }
</>
);
このときchildrenの型があってないと、
return cloneElement(child, { map });
のmapで怒られます。
(上のほうで後々怒られると書いたやつ)
この呼び出しに一致するオーバーロードはありません。
前回のオーバーロードにより、次のエラーが発生しました。
型 '{ map: google.maps.Map | undefined; }' の引数を型 'Partial<unknown> & Attributes' のパラメーターに割り当てることはできません。
オブジェクト リテラルは既知のプロパティのみ指定できます。'map' は型 'Partial<unknown> & Attributes' に存在しません。ts(2769)
ここまでできると最初の画像のようにマーカー付きの地図ができあがります!
ちなみにこの地図の場所は北都システム本社です
Discussion