こんにちは、広野です。
2022年3月29日に、React のバージョン 18 がリリースされました。
本記事を書いている時点では 18.1.0 がリリースされています。本記事では既存アプリ (v17) からアップグレードしたときに対応したことを紹介します。アップグレードにより潜在的なバグが見つかったため、その修正経験も含めて紹介します。
アップグレード時の修正箇所
公式ドキュメントによる修正箇所
公式には、以下のアップグレードガイドに記載されていることを対処することになります。
実際に私が修正した箇所
実際のところ、一般 React 開発者の場合、公式ドキュメントや公式ブログに書かれていること全てを対応することはないと考えます。React 上級者ですと、当然修正該当箇所は多くなると思いますが。
私の場合、大きく以下 3 点がありました。
- モジュールのアップグレード(公式ドキュメント通り、ただし依存性チェックの対応は必要)
- index.js の修正(公式ドキュメント通り)
- コードの潜在的なバグ修正(想定外、当然公式ドキュメントは助けてくれない・・・)
1.モジュールのアップグレード
とりあえず公式ドキュメント通り。@latest をおまじない的に付けるぐらい。※私は npm を使用してます。
npm install --save react@latest react-dom@latest
ですが、ここで他のモジュールとの依存性チェックが走り、私の場合は以下のようなエラーが出ました。ここは各自の環境によって違いがあります。
npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree npm ERR! npm ERR! While resolving: hirodemy-test6@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.0" from @mui/styles@5.8.0 npm ERR! node_modules/@mui/styles npm ERR! @mui/styles@"*" 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 /home/ec2-user/.npm/eresolve-report.txt for a full report. npm ERR! A complete log of this run can be found in: npm ERR! /home/ec2-user/.npm/_logs/2022-05-28T05_00_15_329Z-debug-0.log
こうなるとアップグレードしない選択肢が出てくるわけですが(v17に戻す)、強行したかったので上記エラーメッセージ内にある npm のオプション、–legacy-peer-deps というのを付けて依存性問題のあるモジュールのインストールを実行します。
私の場合は、試行錯誤の末、以下のモジュールに依存性問題があることがわかり、依存性問題を回避させて再インストールしました。
npm install --save @mui/styles material-table@1.69.3 @material-ui/core --legacy-peer-deps
こうするとエラー無しに(回避しただけ)インストールが進みます。
ちなみに MUI 関連のモジュールです。まだ MUI は React 18 対応版がリリースされていないようで、ネット上の他の技術情報フォーラムサイトでも同様に回避している方がいらっしゃいました。正式対応され次第、モジュールアップデートをしたいところです。
2.index.js の修正
こちらは基本公式ドキュメント通りです。開発物により、細かいコードの差異はあります。render の文法が変わっています。
- React 17 仕様の index.js
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; ReactDOM.render( <App />, document.getElementById('root') );
- React 18 仕様の index.js
import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <App /> </React.StrictMode> );
ちなみに本記事執筆時点では create-react-app が自動生成してくれる index.js は React 18 仕様になっていました。
React 18 環境で React 17 仕様の index.js は動作しましたので、不安な方はまずはコードをそのまま置き換えることから始めるのも有りだと思います。
3.コードの潜在的なバグ修正
上記 1. 2. を実施してビルド、デプロイして実際にアプリの動作を試したところ、以下のようなエラーが発生する箇所がいくつかありました。
TypeError: Cannot read property 'XXXXX' of undefined
TypeError: Cannot read property 'XXXXX' of null
いずれも、あるコードが参照する変数等の中に値が入っていない、という状態を示しています。
これまで動作していたのに何故???
おそらく、React 18 になって render 処理が変わり、React 17 ではたまたま発動しなかったバグが表面化しただけだと後から気付きました。
私の場合、以下の 2 パターンの修正をしました。
パターン 1 :画面に表示する state に値が格納されているかチェックを入れる
- 修正前
{(quizOptions.A) && ( <ThemeProvider theme={optionStatus.A.theme}> <Button variant={optionStatus.A.variant} onClick={() => checkAnswer("A")}> {quizOptions.A} </Button> </ThemeProvider> )}
- 修正後
{(quizOptions.A && optionStatus.A) && ( <ThemeProvider theme={optionStatus.A.theme}> <Button variant={optionStatus.A.variant} onClick={() => checkAnswer("A")}> {quizOptions.A} </Button> </ThemeProvider> )}
修正箇所は先頭行だけです。
これまで、quizOptions という state に値が入っているかだけをチェックして表示有無を判断させていたのですが、実際には表示内容の処理には optionStatus という state も含まれます。
React 18 では、optionStatus に値が格納されていない旨のエラーが発生してしまいました。見た目上は state に値が格納され次第、再レンダーされるので実害はないのですが、初回レンダー時にはエラーが発生してしまいます。React 17 では、レンダータイミングの違いなのか、エラーは発生していなかったです。
quizOptions.A と optionStatus.A の両方に値が入っているときだけボタンを表示させる、というコードに修正しました。
パターン 2 :明示的に前処理完了後に後処理を実行させる
言わんとしていることは以下の図の通りです。
元々、私が書いたコードに問題があったのですが、React 17 ではうまく動いているように見えていました。(実際、期待していた通りの動きをしていました)
React 18 になって、誤ったコードの通り忠実に処理され、問題が顕在化しました。
処理順序を守らせるために、useEffect を使用して state 更新後に後続処理を発動させるように修正しました。
ちなみに React 18 へのアップグレード後環境で React 17 仕様の index.js を使用したところ、問題のあるコードでエラー無く動きました。やはり render の内部処理が変わったために動きに違いが出ているものと思います。
まとめ
いかがでしたでしょうか?
このような対応を経て、無事? React アプリが正常に動きました。
まだ React 18 の情報は少なく、本記事は一例ではありますが、似たようなことでお困りの方にとってお役に立てましたら幸いです。
コーディングミスがなければ、React 18 へのアップグレードは問題なく進むと思います。