🦨

ReactのWebアプリでマップ表示と住所検索

2024/07/09に公開

はじめに

実装した画面はこんな感じです。
テキストボックスに住所を入力して ”search” を押すと該当する場所のマップが表示されます。

コンポーネントの最終的なコードはこちら。
https://github.com/t-aono/react-map-sample/blob/main/src/MyMap.tsx

React, Google Maps API を使ってマップ表示をする際、以下のライブラリを使った方法をよく見かけます。

  • google-map-react
  • react-google-maps

これらを使って実装すると Error や Warning が発生してしまいました。
Google Maps API のアップデートに追いついてないのが原因のようです。
そこで、今回は上記のライブラリを使わない方法で実装をやってみました。

事前準備

以下の作業は完了済みの前提です。

  • Google Cloud にてプロジェクト作成
  • APIとサービスの画面から以下のAPIを有効化
    • Maps JavaScript API
    • Geocoding API
  • 認証情報の画面から API Key をコピー

手順

Reactプロジェクト作成。

npm create vite@latest

マップ表示のために、Google Maps JavaScript API を利用します。
API script の Loader が用意されているの、そちらをインストールして使います。

npm install @googlemaps/js-api-loader && npm i -D @types/google.maps

package.json の中身はこんな感じです。
https://github.com/t-aono/react-map-sample/blob/main/package.json

.env ファイルを作成して API Key を貼り付けます。

VITE_MAPS_API_KEY="**********"

公開したくないので、.gitignore に .env を追記しておきます。

まずは、コンポーネントを作成してマップを表示してみます。

import { useEffect, useRef } from "react";
import { Loader } from "@googlemaps/js-api-loader";

export const MyMap = () => {
  const position = { lat: 35.710063, lng: 139.8107 };
  const mapRef = useRef<HTMLDivElement>(null);

  // API Key を使ってローダーを初期化
  const loader = new Loader({
    apiKey: import.meta.env.VITE_MAPS_API_KEY,
    version: "weekly",
  });

  // 初回レンダリング時に使用するライブラリを非同期でロード
  useEffect(() => {
    (async () => {
      const [{ Map }, { AdvancedMarkerElement }] = await Promise.all([
        loader.importLibrary("maps"),
        loader.importLibrary("marker"),
      ]);
      // 地図の描画
      const map = new Map(mapRef.current!, {
        center: position,
        zoom: 10,
        mapId: "DEMO_MAP_ID",
      });
      // マーカーの描画
      new AdvancedMarkerElement({ map, position, title: "" });
    })();
  }, []);

  return (
    <>
      <div ref={mapRef} style={{ height: "400px", width: "400px" }}></div>
    </>
  );
};

ブラウザで見るとこんな感じです。

次に、ジオコーディング API を使って入力された住所の検索をやってみます。
上記のコードに少し変更を加えます。

// ステート化
const [position, setPosition] = useState({ lat: 35.710063, lng: 139.8107 });

// ステートの変更検知
useEffect(() => {
  ・・・  
}, [position]);

// フォームハンドラーを追加
async function submit(event: FormEvent<HTMLFormElement>) {
  event.preventDefault();
  const formData = new FormData(event.currentTarget);
  const address = String(formData.get("address"));

  const { Geocoder } = await loader.importLibrary("geocoding");
  const geocoder = new Geocoder();
  geocoder.geocode({ address }, (results, status) => {
    if (results) {
      const { lat, lng } = results[0].geometry.location;
      if (status === "OK") {
        setPosition({ lat: lat(), lng: lng() });
      }
    }
  });
}

// フォームを追加
return (
  <>
    ・・・  
    <form onSubmit={submit} className="mt-4">
      <input type="text" name="address" />
      <button>search</button>
    </form>
  </>
);

ブラウザで見るとこんな感じになります。

参考

Reactプロジェクト作成
https://ja.vitejs.dev/guide/

Google Maps JavaScript API Loader
https://www.npmjs.com/package/@googlemaps/js-api-loader

Maps JavaScript API
https://developers.google.com/maps/documentation/javascript/overview?hl=ja

マーカーの配置
https://developers.google.com/maps/documentation/javascript/adding-a-google-map?hl=ja

ジオコーディング
https://developers.google.com/maps/documentation/javascript/geocoding?hl=ja

Discussion