ReactでLeafletの地図ライブラリを使う

# はじめに
[JavaScript 地図ライブラリ Leaflet 基本処理サンプル](https://qiita.com/naozo-se/items/d7afe55f139fd712cefa)のReact版です。
Reactになると、結構考え方が違うので、忘れないようにメモ。

# パッケージ追加
```
yarn add leaflet react-leaflet
```

# フォルダ構成
```
src
 ├ index.js 
 ├ App.js
 ├ util
 │  └ leafletCommon.js
 └ mapIcon
     ├ icon1.png
     └ icon2.png
```

# フォルダ＆ファイル構成のざっくり説明
 - util/leafletCommon.js は共通設定とか共通処理(現在地取得しかありませんが)とか
 - mapIcon はアイコン画像を格納しているフォルダ(この辺り良いパッケージがなかった、、、)
 - App.js 実際ページ(コンポーネント上で)上でどう使うか
<br>※indexはデフォルトのやつです

# コード
```react:util/leafletCommon.js
// 地図の設定
export const mapOption = {
  startZoom: "13", // 開始時のズーム値
  maxZoom: "18", // 最大のズーム値
  minZoom: "5", // 最小のズーム値
};

// 現在地(緯度経度取得)関数
export const getCurrentPosition = () =>
  new Promise((resolve, reject) =>
    navigator.geolocation.getCurrentPosition(resolve, reject)
  );
```

```react:App.js
import React, { useState, useEffect } from "react";
import "leaflet/dist/leaflet.css"; 
import Leaflet from "leaflet";
import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet";

import { mapOption, getCurrentPosition } from "./util/leafletCommon";

// 切り替え前データ
const tempPlaceData1 = [
  { id: "1", lat: 33.59513931435894, lng: 130.42419433593753, name: "地点Ａ" },
  { id: "2", lat: 33.59260123175435, lng: 130.41131973266604, name: "地点Ｂ" },
  { id: "3", lat: 33.59517506146791, lng: 130.42694091796878, name: "地点Ｃ" },
  { id: "4", lat: 33.59653344063089, lng: 130.420138835907, name: "地点Ｄ" },
  { id: "5", lat: 33.592813804823924, lng: 130.42249917984012, name: "地点Ｅ" },
  { id: "6", lat: 33.590849553725455, lng: 130.4186797142029, name: "地点Ｆ" },
];
// 切り替え後データ
const tempPlaceData2 = [
  { id: "7", lat: 33.55513931435894, lng: 130.40419433593753, name: "地点Ｇ" },
  { id: "8", lat: 33.59260123175435, lng: 130.42131973266604, name: "地点Ｈ" },
  { id: "9", lat: 33.57517506146791, lng: 130.43694091796878, name: "地点Ｉ" },
  { id: "10", lat: 33.58653344063089, lng: 130.390138835907, name: "地点Ｊ" },
];

// 現在地アイコン
const currentIcon = Leaflet.icon({
  iconUrl: require("./mapIcon/icon1.png"),
  iconSize: [40, 40],
});
// 場所アイコン
const placeIcon = Leaflet.icon({
  iconUrl: require("./mapIcon/icon2.png"),
  iconSize: [40, 40],
});

const App = () => {
  // キー設定
  const [mapKey, setMapKey] = useState(0);
  // 現在地情報
  const [currentPosition, setCurrentPosition] = useState({
    lat: 0,
    lng: 0,
  });
  // 場所情報
  const [placeData, setPlaceData] = useState([]);

  // 初期処理
  useEffect(() => {
    moveCurrentPosition();
    setPlaceData([...tempPlaceData1]);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // 現在地に移動
  const moveCurrentPosition = async () => {
    const location = await getCurrentPosition();
    setCurrentPosition({
      ...currentPosition,
      lat: location.coords.latitude,
      lng: location.coords.longitude,
    });
    // キーを設定して、再表示
    setMapKey(new Date().getTime());
  };

  // 検索処理
  const getLocationList = () => {
    // データ設定
    setPlaceData([...tempPlaceData2]);
    // 本当はfetchとかしてデータ取ってくる
    // const responce = await axios.get("http://localshot:8000/api/getLocation/...");
    // setPlaceData([...responce]);
  };

  return (
    <>
      {/* ボタン(機能操作) */}
      <div>
        <button onClick={() => moveCurrentPosition()}>現在地</button>
        <button onClick={() => getLocationList()}>検索</button>
      </div>
      {/* 地図表示 */}
      <MapContainer
        key={mapKey}
        center={currentPosition}
        zoom={mapOption.startZoom}
        style={{ height: "90vh", width: "100vw" }}
      >
        {/* 地図のタイル情報 */}
        <TileLayer
          attribution='&amp;copy <a href="http://osm.org/copyright";>OpenStreetMap</a> contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
          maxZoom={mapOption.maxZoom}
          minZoom={mapOption.minZoom}
        />
        {/* 現在地情報を出力 */}
        <Marker position={currentPosition} icon={currentIcon}>
          <Popup>現在地</Popup>
        </Marker>
        {/* 場所情報を出力 */}
        {placeData.length > 0
          ? placeData.map((item) => (
              <Marker key={item.id} position={item} icon={placeIcon}>
                <Popup>{item.name}</Popup>
              </Marker>
            ))
          : null}
      </MapContainer>
    </>
  );
};

export default App;
```

# こんな感じ
![leaflet.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/541739/0c02795b-3475-da64-cb86-90bc17b818cf.gif)

# 最後に
自宅でアップすると、家の場所をさらした記事が出来上がってしまうので、危なかった、、
あと、画像のアップロード2M制限とかあったのか、、知らんかった