SmartHR Tech Blog

SmartHR 開発者ブログ

React v18/v19が混在するモノレポで起きたバージョン不一致エラーへの対処

こんにちは、SmartHR の基本機能の開発を担当しているプロダクトエンジニアの sakata です。

先日、担当しているプロダクトのフロントエンドテストフレームワークを Jest から Vitest へ移行しました。 その過程で直面した、モノレポ(monorepo)における React バージョンの不整合に関するエラーとその解決方法について解説します。

モノレポの構成と React バージョンの混在状況

私が担当しているプロダクトでは pnpm の Workspace という機能を使って1つのリポジトリで複数のプロダクトのコードを管理するモノレポの形を取っています。

ディレクトリ構造はざっくりとですが以下のようになっています。

.
├── package.json
├── pnpm-workspace.yaml
└── apps
    ├── app1 (react@18)
    │   └── package.json
    └── app2 (react@19)
        └── package.json

共通して使用するライブラリはルートの package.json に定義し、各プロダクトでのみ使用するライブラリはそれぞれの package.json に定義しています。 また、各プロダクトで使用している React のバージョンは異なっています。

Vitest実行時に発生したエラー

app1 のフロントエンドテストフレームワークを Jest から Vitest へ移行しようとしたところ、Vitest の実行時に以下のエラーが発生しました。

# テスト実行時のエラー(一部抜粋)
A React Element from an older version of React was rendered. This is not supported. It can happen if:
- Multiple copies of the "react" package is used.
- A library pre-bundled an old copy of "react" or "react/jsx-runtime".
- A compiler tries to "inline" JSX instead of using the runtime.

う〜ん、何が起きているんだろう。React のバージョンが原因になっていそうということは分かります。 アプリケーション自体は問題なく動くので、テストの実行環境に問題がありそうです。

エラー原因の調査と分析 ── pnpm whyによる依存関係の解明

はじめに、エラーの内容から app1app2 で使用している React のバージョンを揃えることで解消されるのではないかと考えました。 app2 で使用している React を v18 に戻すのは避けたいので、app1 で使用している React を v19 にアップデートするのが理想的です。 しかしながらプロダクトの規模が大きく、一朝一夕でアップデートすることが難しいので、もう少し根本原因を深ぼってみることにしました。

pnpm why コマンドで依存関係を調査

pnpm why コマンドを使って React のバージョンがどのように解決されているか調べてみました。 以下は pnpm why コマンドで出力されるログの内容を便宜的に修正したものです。

% pnpm why react -r

ルートの package.json で定義しているライブラリ

devDependencies:
@testing-library/react 16.3.0
├── react 19.1.0 peer
└─┬ react-dom 19.1.0 peer
  └── react 19.1.0 peer
(省略)

app1の package.json で定義しているライブラリ

dependencies:
@dnd-kit/core 6.3.1
├─┬ @dnd-kit/accessibility 3.1.1
│ └── react 18.3.1 peer
├─┬ @dnd-kit/utilities 3.2.2
│ └── react 18.3.1 peer
├── react 18.3.1 peer
└─┬ react-dom 18.3.1 peer
  └── react 18.3.1 peer
(省略)

上記を見ると、ルートの package.json に定義している @testing-library/react が React v19 に依存しており、app1 で定義しているライブラリは React v18 に依存していることが分かります。 app1 のテストコードでは @testing-library/react を利用しているため、テスト実行時に要素を作成する処理は React v18、要素を描画する処理は React v19 で行われることになり、React バージョンの不整合が起き、エラーが発生したと考えられます。 このことから、アプリケーション自体は問題なく動作し、テスト実行時にのみエラーが起きるという点も納得です。

解決策 ── Reactに依存するライブラリの配置場所変更による解決

今回は、React に依存しているライブラリを各プロダクトの package.json に定義するという解決方法を取りました。 これにより、@testing-library/react が React v18 に依存するようになり、テスト実行時のエラーが解消されました。

% pnpm why react -r --filter app1

app1の package.json で定義しているライブラリ

dependencies:
@dnd-kit/core 6.3.1
├─┬ @dnd-kit/accessibility 3.1.1
│ └── react 18.3.1 peer
├─┬ @dnd-kit/utilities 3.2.2
│ └── react 18.3.1 peer
├── react 18.3.1 peer
└─┬ react-dom 18.3.1 peer
  └── react 18.3.1 peer

devDependencies:
@testing-library/react 16.3.0
├── react 18.3.1 peer
└─┬ react-dom 18.3.1 peer
  └── react 18.3.1 peer

おわりに

今回発生したエラーの原因を調査する過程で、pnpm の Workspace 機能の仕組みや pnpm why コマンドの使い方など、多くの学びがありました。 本記事が同じ現象でお困りの方々の解決の一助になれば幸いです。

We Are Hiring!

SmartHR では一緒に SmartHR を作りあげていく仲間を募集中です!

少しでも興味を持っていただけたら、カジュアル面談でざっくばらんにお話ししましょう!

hello-world.smarthr.co.jp