kubell Creator's Note

ビジネスチャット「Chatwork」のエンジニアのブログです。

ビジネスチャット「Chatwork」のエンジニアのブログです。

読者になる

Reactをv18にするまでの流れとぶつかった壁

はじめまして!4月に新卒として入社したフロントエンド開発部の山下(yamakenji24)です。Creator's Noteには初投稿となります。

突然ですが皆さん、Reactを使用していますか? ChatworkではjQueryのコードが部分的に動いているものの、大部分がReactで動いております。

そんなReactですが、メジャーアップデートであるv18が発表されて半年が経過し、他の依存するライブラリもReact v18に対応するものが増えてきました。

Reactの恩恵を十分に受けたいのと、追加された機能を検討していきたいというお気持ちから、Reactをv18にあげていくことになりました。

本記事では、React v18化を進めるに当たってぶつかった壁などを紹介できたらなと思います!

なお、「Chatwork Product Day 2022」が10/07(金)から開催されます!それに合わせて、弊社社員がほぼ毎日ブログを投稿しており、本記事もその一環となります。イベント登録がまだの方、ぜひご登録よろしくお願いします!

lp.chatwork.com

React18に上げるための調査

Reactをv18にあげるにあたって、まずは変更された仕様 について確認していきます。 今回の変更で主に対応が必要そうな箇所として、以下の2つが挙げられました。

  • バッチ処理が最適化されたこと

  • useEffectの挙動が変わったこと

また、これらの変更は新しく導入されたcreateRootを適用させない限りは反映されないということが分かりました。

Chatworkのアプリケーションは11周年を迎えており、非常に巨大なプロダクションコードとなっています。 依存するライブラリも多々存在しており、その一つとしてreact-reduxもReact v18に対応したものにあげる必要がありました。
しかし、既存実装の都合上、対応すべき範囲が非常に広いことが事前に判明していました。

以上の点を踏まえた対応方針として、まずは新しく追加された仕様を利用せず、React v17と同様の挙動を担保したままReact v18に上げていくことで影響範囲を小さくすることにしました。(別枠でReact v18の機能を使えるようにする対応も行っていきたいと考えています)

依存ライブラリの更新

最小の変更でReact v18にするには、Storybookをv6.5にあげる@testing-library/reactをv13.0.0にあげるの2つを行えばよさそうでした。

@testing-library/reactはv13.1.0以上に上げることで、@testling-library/react-hooksが統合されるようになるため、当時最新だったv13.3.0を入れることにしました。 また、Domをテストするrenderも同様にlegacyRoot: trueを付与してReact v17と同じ挙動を担保するようにします。

Storybookはv6.5に上げる際に、legacyRootApiを設定に付与することでReact v17と同じ挙動を担保することができます。

@testing-library/reactをv13.3.0へ上げることはうまくいきましたが、Storybookをv6.5へ移行するタイミングで詰まったポイントがありました。

Storybookのスナップショットテストを行うために、@storybook/addon-storyshotsを導入しているのですが、 そのテストが全て以下のエラーで落ちるようになってしまいました。

TypeError: Cannot read properties of undefined (reading 'current')

調査をしていくと、同様の現象で落ちている報告がStorybookのissueで挙げられていました。 どうやらライブラリの内部で依存しているreact-test-rendererがv17までしか対応しておらず、Storybookをv7まで上げるか、バージョンを固定するかの解決策が提示されていました。

Storybook v7.0.0-alpha.16から対応が入っているものの、現時点ではStorybookをv7系に更新するための対応範囲が広く、すぐに対応できませんでした。 そのため、内部で使用している依存のバージョンを固定する方法を取ることにしました。
使用しているバージョンに脆弱性が発生している場合などに強制的に別バージョンに固定などを検討することがありますが、今回はStorybookをv7系に対応させるまでの一時的な対応として使用していくことにしました。

依存するバージョンを固定するために、yarnではデフォルトでresolutionsが提供されており、npmでは別途npm-force-resolutionsを入れる必要があると当初は思っていました。 (後述しますが、完全に調査不足だったなと思います)

build-storybookでのエラー対応

実際にreact-test-rendererのバージョンをv18に固定したところ、ビルドとテストが通るようになりました。 しかし、手元で動作検証を行った後、社内検証に出そうとしたところで今回はbuild-storybookでこけてしまいました。

エラー内容はERR! Cannot read properties of undefined (reading 'then')と出ており、terser-webpack-pluginなどでこけていそうな雰囲気がありました。 そこで、@storybook/addon-storyshotsの依存が正しくないのか、babelの設定でこけているのかなと考えました。

結論を言うと、npm-force-resolutionsによって固定した依存関係が正しく配置されていなかったことが原因でした。

おそらく、npm-force-resolutionsの最終メンテ日が2021/02だったことから、対象npmのversionが7系と推測され、 使用しているnpm v8系に対応していなかったのではないかなと思われます。

npm v8.3.0から導入されたバージョンを固定するためのoverridesに修正したところ、無事ビルドが通るようになりました! (npm難しい....)

まとめ

今回は、React v18に上げるためにやったこととぶつかった壁の一部をご紹介させていただきました! 事前の調査不足やnpmに関する知識や経験が少し乏しかったところもあり、エラーの特定に戸惑ってしまった場面もありましたが、比較的影響範囲が少なくReact v18へ上げることができたのではないかと思います。

また、今回はReact v18化の一部であり、react-reduxをv8系に上げたり、Storybookをv7系に上げたり、まだまだ対応すべきことは山ほどあります。 追加された機能などを用いて、どう開発体験を向上させていくか、ユーザーが使いやすくなるためにどうしていくか、難しいながらも日々試行錯誤しながらやっていくのは非常に楽しいです!

最後まで読んでくださり、ありがとうございました!🙇‍♀️