本日から3日間は外部APIや media element を利用した変わりダネを紹介します。今日は Google Map を埋め込み、現在地を表示するサンプルです。$ yarn 1219
しただけでは挙動確認が出来ないので、以下手順でセットアップしてからお試し下さい。
code: github / $ yarn 1219
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の場合)
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 に分岐します。
export default () => (
<GeolocationWrapper
renderProcessView={() => <ProcessView />}
renderSuccessView={coords => (
<GoogleMapView coords={coords} />
)}
renderErrorView={() => <ErrorView />}
/>
)
navigator.geolocation.getCurrentPosition
を call すると、現在位置取得の許可が求められます。現在位置もエラーも null なら進行中の View を、エラーがあればエラー View を、現在位置取得が出来たら GoogleMap API 利用の View をマウントします。
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 の更新関数で再描画させます。
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>
)
}