Next.jsプロジェクトでReact Leafletを使って地図を表示してみた
こんにちは!DA(データアナリティクス)事業本部 サービスソリューション部の大高です。
最近Next.jsを触っているのですが、ふと「地図も表示してみたいな」と思ったので試してみることにしました。
地図表示が出来るライブラリとしては様々なライブラリがあると思いますが、React Leafletというライブラリが利用できそうなので今回はこちらを利用して試してみたいと思います。
React Leafletについて
有名なオープンソースの地図表示ライブラリとしてLeafletというライブラリがあります。
このライブラリはJavaScriptライブラリとなっており、Reactプロジェクトでもこのライブラリを直接読み込んで利用することもできるかと思います。一方でReact向けのReact Leafletというラッパーライブラリが存在します。
こちらのReact Leafletを利用するとReactプロジェクトでも地図表示がしやすそうだったので、こちらを利用して地図表示をしてみたいと思います。
やってみる
ではまずはNext.jsのプロジェクト作成からやっていきます。
Next.js プロジェクトの作成
create next-app
を利用して作成していきます。プロジェクト名はhello-react-leaflet
にしました。
% yarn create next-app --typescript yarn create v1.22.17 [1/4] ? Resolving packages... [2/4] ? Fetching packages... [3/4] ? Linking dependencies... [4/4] ? Building fresh packages... success Installed "[email protected]" with binaries: - create-next-app ✔ What is your project named? … hello-react-leaflet (...snip...) We suggest that you begin by typing: cd hello-react-leaflet yarn dev ✨ Done in 45.28s.
Leaflet関連パッケージのインストール
以下のドキュメントを参考に必要なパッケージをインストールしていきます。
今回はNext.jsを利用しており、TypeScriptを利用する設定にしたので最終的に以下のコマンドでインストールをします。
% yarn add leaflet react-leaflet % yarn add -D @types/leaflet
なお、ドキュメントには以下のように事前準備としてLeaflet(JavaScript版)のクイックスタートガイドに沿って事前準備をするように記載があるのですが、実際には特に実施することはありませんでした。
Before using React Leaflet, you must setup your project following Leaflet's Quick Start Guide.
Mapコンポーネントの作成とページの作成
インストールが済んだので、実際に地図を表示するためのコンポーネントを作成していきます。
Stack Overflowにとても参考になる回答があったので、こちらを参考に作成してみます。
まずはコンポーネントとして、map.tsx
を作成します。上記のStack Overflowの回答にもありますが、コンポーネントとして作成しないとwindowの未定義エラー window is not defined
が発生するので、コンポーネントとして作成してページから参照するようにします。
import { MapContainer, Marker, Popup, TileLayer } from "react-leaflet"; import "leaflet/dist/leaflet.css"; const Map = () => { return ( <MapContainer center={[51.505, -0.09]} zoom={13} scrollWheelZoom={false} style={{ height: "100vh", width: "100%" }} > <TileLayer attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> <Marker position={[51.505, -0.09]}> <Popup> A pretty CSS3 popup. <br /> Easily customizable. </Popup> </Marker> </MapContainer> ); }; export default Map;
コンポーネントでは、地図を全画面表示して画面の中央にマーカーも表示してあります。
更に、このコンポーネントを利用するページも作成していきます。
import dynamic from "next/dynamic"; import React from "react"; function MapPage() { const Map = React.useMemo( () => dynamic(() => import("../components/map"), { loading: () => <p>A map is loading</p>, ssr: false, }), [] ); return <Map />; } export default MapPage;
このコードでは、コンポーネントの再描画のチラつきを抑えるためにReact.useMemo
hookを利用しています。また、dynamic
を利用して動的にコンポーネントをインポートし、更にサーバーサイドレンダリングをしない(ssr: false
)ようにすることで、クライアントサイドに依存するコンポーネントを利用できるようにしています。
dynamic
を利用したコードは最初はよく理解できなかったのですが、以下のエントリを参考にさせていただくことで意味が理解できました。
ドキュメントにもサーバーサイドレンダリングには対応していない旨の注意書きがされていますね。
Leaflet makes direct calls to the DOM when it is loaded, therefore React Leaflet is not compatible with server-side rendering.
地図を表示してみる
では、実際に動かして表示してみます。
% yarn dev yarn run v1.22.17 $ next dev ready - started server on 0.0.0.0:3000, url: http://localhost:3000
起動後に、http://localhost:3000
にアクセスするとサンプルアプリの画面が表示されます。
今回は新しく作成した地図表示用のページを確認したいのでhttp://localhost:3000/map-page
にアクセスします。
表示されました!
が、一見良さそうに見えますがマーカー表示が壊れていますね。本来であれば画面中央にマーカーを表示したいです。
壊れたマーカー表示を直す
GitHubのIssueにずばり該当するものがあったので、こちらでコメントされていた方法で対応してみます。
import { MapContainer, Marker, Popup, TileLayer } from "react-leaflet"; import "leaflet/dist/leaflet.css"; import L from "leaflet"; import markerIcon2x from "leaflet/dist/images/marker-icon-2x.png"; import markerIcon from "leaflet/dist/images/marker-icon.png"; import markerShadow from "leaflet/dist/images/marker-shadow.png"; delete L.Icon.Default.prototype._getIconUrl; L.Icon.Default.mergeOptions({ iconUrl: markerIcon.src, iconRetinaUrl: markerIcon2x.src, shadowUrl: markerShadow.src, }); const Map = () => { return ( <MapContainer center={[51.505, -0.09]} zoom={13} scrollWheelZoom={false} style={{ height: "100vh", width: "100%" }} > <TileLayer attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> <Marker position={[51.505, -0.09]}> <Popup> A pretty CSS3 popup. <br /> Easily customizable. </Popup> </Marker> </MapContainer> ); }; export default Map;
map.tsx
にハイライト部分のコードを追加して、マーカーアイコンのURLを上書き修正しています。
今度はうまくいきました!想定どおりマーカーが表示されましたね。
まとめ
以上、Next.jsプロジェクトでReact Leafletを使って地図を表示してみました。
いくつかNext.jsならではの対応がありましたが、そこだけ対応できればReact Leafletを利用して地図表示ができました。あとはLeafletに慣れていれば問題なく地図表示ができそうですね。
どなたかのお役に立てば幸いです。それでは!