LoginSignup
6
6

More than 5 years have passed since last update.

本日から3日間は外部APIや media element を利用した変わりダネを紹介します。今日は Google Map を埋め込み、現在地を表示するサンプルです。$ yarn 1219 しただけでは挙動確認が出来ないので、以下手順でセットアップしてからお試し下さい。
code: github / $ yarn 1219

1219-1.jpg 1219.jpg

setup

  • Maps JavaScript API の APIキーを取得
  • キーにアプリケーションの制限をかける(http://localhost:1234/など)
  • キーにAPIの制限をかける(Maps JavaScript API)
  • .env に GOOGLE_MAP_API_KEY=${YOUR_API_KEY} を記述

Google Map API を利用するにあたり、api_key が必要になります。環境変数から script tag に api_key を挿入しています。リポジトリルートに .env を作成し、取得した api_key を置きます。

GOOGLE_MAP_API_KEY=XXXXXX

サンプルには parcel を使用しており、react-async-script で 環境変数から script タグを生成(parcelの場合)

app.tsx
import * as React from 'react'
import { render } from 'react-dom'
import Root from './components/index'
// @ts-ignore
import makeAsyncScriptLoader from 'react-async-script'
const URL = `https://maps.googleapis.com/maps/api/js?key=${
  process.env.GOOGLE_MAP_API_KEY
}`
const AsyncScriptRoot = makeAsyncScriptLoader(URL)(Root)
render(<AsyncScriptRoot />, document.getElementById('app'))

進行状況に応じた View 分岐

geolocation API を利用し、ユーザーの現在位置を利用します。現在位置取得出来るまで、3つの View に分岐します。

components/index.tsx
export default () => (
  <GeolocationWrapper
    renderProcessView={() => <ProcessView />}
    renderSuccessView={coords => (
      <GoogleMapView coords={coords} />
    )}
    renderErrorView={() => <ErrorView />}
  />
)

navigator.geolocation.getCurrentPosition を call すると、現在位置取得の許可が求められます。現在位置もエラーも null なら進行中の View を、エラーがあればエラー View を、現在位置取得が出来たら GoogleMap API 利用の View をマウントします。

components/geolocationWrapper.tsx
export default (props: Props) => {
  const [coords, setCoords] = useState<Coordinates | null>(
    null
  )
  const [error, setError] = useState<PositionError | null>(
    null
  )
  useEffect(() => {
    navigator.geolocation.getCurrentPosition(
      ({ coords }) => {
        setCoords(coords)
      },
      setError
    )
  }, [])
  return (
    <>
      {useMemo(
        () => {
          if (coords === null) return
          return props.renderSuccessView(coords)
        },
        [coords]
      )}
      {useMemo(
        () => {
          if (error === null) return
          return props.renderErrorView(error)
        },
        [error]
      )}
      {useMemo(
        () => {
          if (!(error === null && coords === null)) return
          return props.renderProcessView()
        },
        [coords, error]
      )}
    </>
  )
}

useEffect と GoogleMap API

GoogleMap API に則り、マウント時に取得した現在位置 coords をもって API を叩きます。地図をマウントするタグは ref で参照し、API callback 時に useState の更新関数で再描画させます。

components/googleMapView.tsx
const View = (props: Props) => {
  const [addressLabel, setAddressLabel] = useState(
    '...取得しています'
  )
  const ref = useRef({} as HTMLDivElement)
  useEffect(
    () => {
      if (ref.current === null) return
      const { latitude, longitude } = props.coords // 取得した現在位置
      const center = { lat: latitude, lng: longitude }
      const mapOptions: google.maps.MapOptions = {
        center,
        zoom: 16
      }
      const map = new google.maps.Map(
        ref.current,
        mapOptions
      )
      const markerOptions: google.maps.MarkerOptions = {
        position: center,
        map
      }
      new google.maps.Marker(markerOptions)
      const geocoder = new google.maps.Geocoder()
      geocoder.geocode({ location: center }, function(
        results,
        status
      ) {
        if (status == google.maps.GeocoderStatus.OK) {
          setAddressLabel(results[1].formatted_address)
        } else {
          setAddressLabel('取得に失敗しました')
        }
      })
    },
    [props.coords]
  )
  return (
    <div className={props.className}>
      <div className="map" ref={ref} />
      <p className="addressLabel">
        現在住所
        {addressLabel}
      </p>
    </div>
  )
}
6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6