O hirunewani blog

Upgrade to React 18

Created at

会社にて、ほとんどのサービスのReactのバージョンをアップグレードするイベントを開催した際のQ&Aなどをまとめました。

会社にて、ほとんどのサービスのReactのバージョンをアップグレードするイベントを開催しました。フロントエンドエキスパートとして、エラーを報告を受けQ&Aを作成するなどをしていたので、その立場からパッケージのアップグレードにどう取り組めば良いのか、実際に報告されたエラーとその対応方法についてまとめます。

React 18 アップグレードは怖くない

まず最初にReact 18へアップグレードすることは全く怖くありません。

少なくとも基本的なことをちゃんとやっていれば、どうすればいいか分からないといった状況にはならないと思います。

公式ドキュメントをちゃんと読む

公式ドキュメントはちゃんと読みましょう。

特に公式ドキュメントの1番上に書かれた、この変更を忘れたことによる報告が多々ありました。

import { createRoot } from "react-dom/client";
const container = document.getElementById("app");
const root = createRoot(container);
root.render(<App tab="home" />);

エラーをちゃんと読む

エラー報告を受けた内2割程度は、エラーメッセージにどうような状況か詳細に書かれたものや、対応について書かれたドキュメントへのリンクを含むものでした。

エラーが出ても落ち着いて、メッセージを読みましょう。失敗しても慌てないというメンタルは大切です。

リポジトリをちゃんと整備する

React以外の使用しているパッケージが古い、推奨されていない記述が残っていることによって発生しているエラー報告は多々ありました。

  • Dependabotやrenovateを導入し、パッケージはなるべく新しい状態を保ちましょう。
  • Deprecatedな記法はなるべく使わないようにしましょう。もし気付けないのであればESLintなどで検知出来る仕組みを作るべきです。
  • 使用しているライブラリには、ちゃんと興味を持ち、どのような利用が推奨されているのかなどを学んでおきましょう。

プルリクエストには余計な変更を入れない

Reactと一緒にreact-router-domなど他のパッケージも上げようとしてしまい、移行が困難になっているケースがありました。

リポジトリがちゃんと整備されていなくても、プルリクエストをちゃんと小さくしようという努力をしていれば大きくつまずくことはないと思います。

  • React 18アップグレードと関係がない変更は別のプルリクエストで行うべきです。
  • React 18アップグレードと関係がある変更でも、分割できないか考えましょう。
  • React 18に関連してパッケージをアップグレードする際には、React 18サポートをしつつ17をドロップしていないバージョンがあれば、そのパッケージのみを上げるプルリクエストをまず投げましょう。

エラー報告と対応

作成したQ&Aを元に、報告されたエラーとその対応方法や未前に防ぐ方法について書きます。

Property ‘children’ does not exist

FCからchildrenが削除されたことに起因している可能性があります。

Incorrect

const MyComponent: React.FC = () => {};
return (
  <MyComponent>
    <div>Child component</div>
  </MyComponent>
);

Correct

const MyComponent = (props: { children: React.ReactNode }): JSX.Element => {};
return (
  <MyComponent>
    <div>Child component</div>
  </MyComponent>
);

Childrenの型を書くのが面倒

@types/reactからPropsWithChildrenがexportされています。

const MyComponent = (
  props: PropsWithChildren<MyComponentProps>
): JSX.Element => {};
return (
  <MyComponent>
    <div>Child component</div>
  </MyComponent>
);

VFCを使うべきか

FCからchildrenが削除されたことにより、React 18からVFCは非推奨になりました。 そもそもVFCは、FCからpropsを削除するという破壊的変更を避けるために追加されたもので、FCの変更が行われた今となっては不要なものというわけです。

FCを使うべきか

個人的にもおすすめしませんし、ReactコミュニティとしてFCを使わない流れがあります。

例えばfacebook/create-react-appでも、ジェネリクスを利用したジェネリックコンポーネントを扱えない、“component as namespace pattern”の型が複雑になる、defaultProps(既にdefaultProps自体が非推奨ですが)が上手く動作しないなどの理由によりTypescriptテンプレートからFCが削除されています。

Ref: Remove React.FC from Typescript template #8177

SomeComponent cannot be used as a JSX component

使用しているライブラリが、@types/reactの17系に依存している可能性があります。

まずは依存していないか、またどのライブラリが依存しているのかを調べましょう。

npm ls @types/react

依存しているライブラリのリリースノートを確認した上で問題がなさそうであれば、アップグレードしましょう。

yarn upgrade [some package] --latest

それでも治らない、またはアップグレードが困難である場合はpackage.jsonに次のような記述を加えて@types/react 18を強制します。 バージョンの指定は現在利用している18系のものに書き換えてください。

"resolutions": {
    "@types/react": "18.0.12"
}

この記述をした上で再度パッケージのインストールを実行してください。

yarn install

これを忘れたことによる治らないというエラー報告もありました。

error TS1005: ’,’ expected.

Typescriptのバージョンが古く、export { type Hoge }といった記法をサポートしていない可能性があります。

Typescriptのバージョンを上げてください。

yarn upgrade typescript --latest

BrowserRouterで型エラーが出る

パッケージによっては、主にFCなどへの対応で型のアップグレードが必要です。

react-router-dom 5の場合、@types/react-router-domのバージョンを上げてください。

yarn upgrade @types/react-router-dom --latest

react-router-dom 6の場合は、react-router-dom内に型が含まれているので、react-router-domのバージョンを上げてください。

ローカル環境を立ち上げるコマンドが失敗する

報告を受けた例では、webpack-cliのバージョンと@webpack-cli/serveの有無によって発生していました。

そもそも@webpack-cli/serveは不要でしたが、React 18対応をする中で、いくつのもパッケージをインストールしたり、ロックファイルを再生成する中でwebpack-cliのバージョンが上がりエラーとして露見したようです。webpack-cliを最新に上げれば解決します。

このような問題は、利用しているパッケージのメンテナンスの他、バージョンの指定をPin dependenciesにしておくことでも回避できると思います。