👻

yarnを使ったパッケージインストール時に警告をちゃんと読んでおくと余計なトラブルを避けられる 〜react-leafletで苦労した話〜

2022/05/07に公開

はじめに

yarn install/add 実行中の警告表示を特に気にしていなかったことで、あとで余計な調査が発生したという経験をしたため、反省の意味を込めて記事にしました。

next.js + react-leafletを使うときに困った事例

このお話は2022/4初旬に遭遇したものですが、現在は関連パッケージの更新が完了しており、同じ状況にはなりません。
記事中のコマンド群やソースコードは記事執筆にあたり再現性があるように調整しています。

準備

とあるwebページ制作案件でページ内に地図を表示するという要件があったため、 next.js(v12.1.4) + react-leafletを利用する機会がありました。
当時やったこととしては、以下のコマンドを使いnext.jsに対してreact-leafletを追加し、ページ内に地図を埋め込んでいくというものでした。

$ npx create-next-app sample-page --typescript
# 当時はバージョン指定無しで3.2.5が入りました。状況再現のためバージョン指定のコマンドを記載。
$ yarn add leaflet react-leaflet@3.2.5

yarn addを実行したとき、実は以下のようなwarningが出ていたのですが、当時は気づくこともなく作業を進めていました。

[3/4] 🔗  Linking dependencies...
warning " > react-leaflet@3.2.5" has incorrect peer dependency "react@^17.0.1".
warning " > react-leaflet@3.2.5" has incorrect peer dependency "react-dom@^17.0.1".
warning "react-leaflet > @react-leaflet/core@1.1.1" has incorrect peer dependency "react@^17.0.1".
warning "react-leaflet > @react-leaflet/core@1.1.1" has incorrect peer dependency "react-dom@^17.0.1".

問題発生

いざreact-leafletのコンポーネントをページに埋め込んで見ると以下のエラーが表示されました。
error

そのときのコードは以下の通りです。

# index.tsx

import type { NextPage } from 'next'
import { useMemo } from 'react'
import dynamic from 'next/dynamic'

const Home: NextPage = () => {
  const Map = useMemo(
    () =>
      dynamic(() => import('../components/map'), { // react-leafletはCSRでしか動かないためdynamicを使用する
        loading: () => <p>loading</p>,
        ssr: false
      }),
    []
  )
  return (
    <div style={{ height: '100%', width: '500px' }}>
      <Map></Map>
    </div>
  )
}

export default Home
# components/map.tsx

import { MapContainer, TileLayer } from 'react-leaflet'
import 'leaflet/dist/leaflet.css'

function Map(): JSX.Element {
  return (
    <MapContainer
      center={[35.689527394778, 139.6894541598]} // 新宿中央公園を表示する
      zoom={15}
      scrollWheelZoom={false}
      style={{ width: "300px", height: "300px" }}
    >
      <TileLayer
        attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        url="https://tile.openstreetmap.jp/{z}/{x}/{y}.png"
      />
    </MapContainer>
  )
}

export default Map

エラーには「Map container is already initialized」とあるので、Mapコンポーネントが2回以上レンダリングされているように見えますが、
なぜそうなっているのか、はじめは分かりませんでした。

加えて、yarn devだとエラーとなるのですがyarn build && yarn startの場合はエラーとならず地図が表示されていました。
謎は深まるばかり。。。

解決までにやったこと

  1. mapコンポーネントにconsole.logを差し込む
    => console.logが2回実行されているため、mapコンポーネントが2回レンダリングされているのは確定。
  2. dynamicの書き方が悪くて複数回コンポーネントが表示されてしまっている?
    => 先人の知恵をググったりstackoverflowなどでも調べたが、どうやらこの書き方は正しそう。。
  3. 試しにHomeコンポーネントにもconsole.logを差し込む
    => ここでもconsole.logが2回実行されている。ってことは、next.js自体が全てのコンポーネントを2回レンダリングするようにできている?と推測。
  4. next.jsの機能でコンポーネントを2回レンダリングする理由を調べていく
    => どうやらreactのStrictModeが原因。開発モードのみで有効となる機能であるため、本番モードならエラーにならないという事象とも合致。
  5. next.config.jsのreactStrictModeをfalseに修正する(解決方法1)
    => 地図が正しく表示されるようになった!
  6. この解決方法はあまりよくなさそうなので、react-leafletのgithubにissueが上がってないか見にいく
    => react18に対応してほしいというissueを発見
  7. react-leafletのv4.0.0-beta.1の存在を知り、こっちを使ってみる(解決方法2)
    => StrictModeがtrueの状態でも地図が正しく表示されるようになった。そのうちv4の正式バージョンがリリースされるとのことだったので、これにて解決。

色々とやっているうちに3時間くらい経過していました。

最終的にはこんな感じに表示されます。
ok

後で気付いたこと(本記事で書きたかったこと)

yarnで表示されていたwarning表示

それから1ヶ月ほど経過したある日、同リポジトリを別の開発用PCで起動させるためパッケージのインストールなどをしていたところ、記事の冒頭に貼ったyarnのwarning表示がふと目に入りました。

[3/4] 🔗  Linking dependencies...
warning " > react-leaflet@3.2.5" has incorrect peer dependency "react@^17.0.1".
warning " > react-leaflet@3.2.5" has incorrect peer dependency "react-dom@^17.0.1".
warning "react-leaflet > @react-leaflet/core@1.1.1" has incorrect peer dependency "react@^17.0.1".
warning "react-leaflet > @react-leaflet/core@1.1.1" has incorrect peer dependency "react-dom@^17.0.1".

そこには明らかに、react-leaflet@3.2.5はreact17までしか対応していないことを意味するエラーが出ていました。
もしもこれを初期段階で気付いていれば、以下の流れでスムーズに対応でき、時間をかなり節約できていたと思います。
warningを見る -> react18に対応したreact-leafletが無いかgithubを見にいく -> react-leaflet v4の存在を知る&v3で起きている問題点を認識する -> next.jsに組み込む

yarnとnpmのパッケージインストール時の挙動の違い

今回next.jsを使うにあたりyarnを使っていましたが、もしもnpmを使っていた場合以下のようになります。

$ npx create-next-app sample-page2 --typescript --use-npm
$ npm install leaflet react-leaflet@3.2.5

npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! 
npm ERR! While resolving: npm-src@0.1.0
npm ERR! Found: react@18.1.0
npm ERR! node_modules/react
npm ERR!   react@"18.1.0" from the root project
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer react@"^17.0.1" from react-leaflet@3.2.5
npm ERR! node_modules/react-leaflet
npm ERR!   react-leaflet@"3.2.5" from the root project
npm ERR! 
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR! 
npm ERR! See /Users/nyarome/.npm/eresolve-report.txt for a full report.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/nyarome/.npm/_logs/2022-05-07T05_48_30_419Z-debug-0.log

npmの場合はパッケージの依存性に不整合がある場合エラー表示をしてくれるようです。
これだけ分かりやすく表示してくれれば、見落とすことはまずないでしょう。

まとめ

  • yarnを使ってパッケージをインストールする際は警告表示が出てないかをよく見る
    • 一旦放置しても大丈夫なのか、解決しないといけないのかを判断する癖をつける
  • 使いたいライブラリがフレームワークの新バージョンに対応しているか導入前によく確認する必要がある
    • 今回はnext.jsの新バージョンに関するものでしたが、よくある話なので注意
    • 公式のissueをよく読むと大概記載されている

あとがき

最初の段階でwarningに目を通せていたら解決までの時間は節約できましたが、代わりにnext.jsのことを少し余分に学ぶことができました。
ライブラリ選定をするときの基本だと思う(やれてなかったですが)ので、考え方の参考にしていただければと思います。

Discussion