React 19 アップグレードガイド
2024年4月25日 Ricky Hanlon 著
React 19に追加された機能強化には、いくつかの破壊的変更が必要ですが、アップグレードをできるだけスムーズに行えるように努めており、ほとんどのアプリケーションへの影響は少ないと考えています。
この投稿では、React 19へのアップグレード手順について説明します。
React 19のテストにご協力いただける方は、このアップグレードガイドの手順に従って、発生した問題を報告してください。React 19に追加された新機能のリストについては、React 19リリース投稿を参照してください。
インストール
ReactとReact DOMの最新バージョンをインストールするには
npm install --save-exact react@^19.0.0 react-dom@^19.0.0
または、Yarnを使用している場合は
yarn add --exact react@^19.0.0 react-dom@^19.0.0
TypeScriptを使用している場合は、型も更新する必要があります。
npm install --save-exact @types/react@^19.0.0 @types/react-dom@^19.0.0
または、Yarnを使用している場合は
yarn add --exact @types/react@^19.0.0 @types/react-dom@^19.0.0
最も一般的な置換のためのcodemodも含まれています。下記のTypeScriptの変更点を参照してください。
Codemods
アップグレードを支援するために、codemod.comのチームと協力して、React 19の多くの新しいAPIとパターンにコードを自動的に更新するcodemodsを公開しました。
すべてのcodemodsはreact-codemod
リポジトリで利用できます。Codemodチームは、codemodsの維持にも協力しています。これらのcodemodsを実行するには、より高速で、より複雑なコード移行を処理し、TypeScriptをより適切にサポートするため、react-codemod
ではなくcodemod
コマンドを使用することをお勧めします。
コードモッドを含む変更には、以下のコマンドが含まれています。
利用可能なすべてのコードモッドのリストについては、react-codemod
リポジトリを参照してください。
破壊的変更
レンダリング時のエラーは再スローされません
以前のバージョンのReactでは、レンダリング中に発生したエラーはキャッチされ、再スローされていました。開発環境では、console.error
にもログが出力され、エラーログが重複していました。
React 19では、エラー処理の方法を改善し、再スローしないことで重複を減らしました。
- キャッチされないエラー:エラーバウンダリでキャッチされないエラーは、
window.reportError
に報告されます。 - キャッチされたエラー:エラーバウンダリでキャッチされたエラーは、
console.error
に報告されます。
この変更はほとんどのアプリケーションに影響を与えることはありませんが、本番環境のエラーレポートがエラーの再スローに依存している場合は、エラー処理を更新する必要があるかもしれません。これをサポートするために、カスタムエラー処理用の新しいメソッドをcreateRoot
とhydrateRoot
に追加しました。
const root = createRoot(container, {
onUncaughtError: (error, errorInfo) => {
// ... log error report
},
onCaughtError: (error, errorInfo) => {
// ... log error report
}
});
詳細については、createRoot
とhydrateRoot
のドキュメントを参照してください。
非推奨のReact APIの削除
削除されました:propTypes
と defaultProps
(関数用)
PropTypes
は、2017年4月(v15.5.0)に非推奨となりました。
React 19では、ReactパッケージからpropType
チェックを削除し、それらを使用してもサイレントに無視されます。propTypes
を使用している場合は、TypeScriptまたは他の型チェックソリューションへの移行をお勧めします。
関数コンポーネントではES6のデフォルトパラメータの代わりにdefaultProps
も削除します。クラスコンポーネントは、ES6の代替手段がないため、defaultProps
をサポートし続けます。
// Before
import PropTypes from 'prop-types';
function Heading({text}) {
return <h1>{text}</h1>;
}
Heading.propTypes = {
text: PropTypes.string,
};
Heading.defaultProps = {
text: 'Hello, world!',
};
// After
interface Props {
text?: string;
}
function Heading({text = 'Hello, world!'}: Props) {
return <h1>{text}</h1>;
}
削除されました:contextTypes
とgetChildContext
を使用するレガシーコンテキスト
レガシーコンテキストは、2018年10月(v16.6.0)に非推奨となりました。
レガシーコンテキストは、contextTypes
とgetChildContext
APIを使用するクラスコンポーネントでのみ使用可能であり、見落としやすい微妙なバグのためにcontextType
に置き換えられました。React 19では、Reactをわずかに小さく高速化するために、レガシーコンテキストを削除します。
クラスコンポーネントでレガシーコンテキストをまだ使用している場合は、新しいcontextType
APIに移行する必要があります。
// Before
import PropTypes from 'prop-types';
class Parent extends React.Component {
static childContextTypes = {
foo: PropTypes.string.isRequired,
};
getChildContext() {
return { foo: 'bar' };
}
render() {
return <Child />;
}
}
class Child extends React.Component {
static contextTypes = {
foo: PropTypes.string.isRequired,
};
render() {
return <div>{this.context.foo}</div>;
}
}
// After
const FooContext = React.createContext();
class Parent extends React.Component {
render() {
return (
<FooContext value='bar'>
<Child />
</FooContext>
);
}
}
class Child extends React.Component {
static contextType = FooContext;
render() {
return <div>{this.context}</div>;
}
}
削除されました:文字列ref
文字列refは、2018年3月(v16.3.0)に非推奨となりました。
クラスコンポーネントは、いくつかの欠点のためにrefコールバックに置き換えられる前に、文字列refをサポートしていました。React 19では、Reactをよりシンプルで分かりやすくするために、文字列refを削除します。
クラスコンポーネントで文字列refをまだ使用している場合は、refコールバックに移行する必要があります。
// Before
class MyComponent extends React.Component {
componentDidMount() {
this.refs.input.focus();
}
render() {
return <input ref='input' />;
}
}
// After
class MyComponent extends React.Component {
componentDidMount() {
this.input.focus();
}
render() {
return <input ref={input => this.input = input} />;
}
}
削除済み: モジュールパターンファクトリ
モジュールパターンファクトリは、2019年8月(v16.9.0)に非推奨となりました。
このパターンはほとんど使用されておらず、これをサポートすることでReactのサイズがわずかに大きくなり、速度が低下します。React 19では、モジュールパターンファクトリのサポートを削除します。通常の関数に移行する必要があります。
// Before
function FactoryComponent() {
return { render() { return <div />; } }
}
// After
function FactoryComponent() {
return <div />;
}
削除済み: React.createFactory
createFactory
は、2020年2月(v16.13.0)に非推奨となりました。
createFactory
の使用は、JSXの幅広いサポート以前に一般的でしたが、現在ではほとんど使用されておらず、JSXに置き換えることができます。React 19では、createFactory
を削除します。JSXに移行する必要があります。
// Before
import { createFactory } from 'react';
const button = createFactory('button');
// After
const button = <button />;
削除済み: react-test-renderer/shallow
React 18では、react-test-renderer/shallow
を更新し、react-shallow-renderer を再エクスポートしました。React 19では、パッケージを直接インストールすることを推奨するため、react-test-render/shallow
を削除します。
npm install react-shallow-renderer --save-dev
- import ShallowRenderer from 'react-test-renderer/shallow';
+ import ShallowRenderer from 'react-shallow-renderer';
削除済み: 非推奨のReact DOM API
削除済み: react-dom/test-utils
act
はreact-dom/test-utils
からreact
パッケージに移動されました。
ReactDOMTestUtils.act
はReact.act
に置き換えられました。react-dom/test-utils
ではなくreact
からact
をインポートしてください。詳細はhttps://react.dokyumento.jp/warnings/react-dom-test-utilsを参照してください。この警告を解決するには、react
からact
をインポートします。
- import {act} from 'react-dom/test-utils'
+ import {act} from 'react';
その他のtest-utils
関数はすべて削除されました。これらのユーティリティは一般的ではなく、コンポーネントとReactの低レベルの実装の詳細に依存しやすくなっていました。React 19では、これらの関数を呼び出すとエラーが発生し、将来のバージョンではエクスポートが削除されます。
代替手段については、警告ページを参照してください。
削除済み: ReactDOM.render
ReactDOM.render
は2022年3月(v18.0.0)に非推奨となりました。React 19ではReactDOM.render
を削除し、ReactDOM.createRoot
を使用する必要があります。
// Before
import {render} from 'react-dom';
render(<App />, document.getElementById('root'));
// After
import {createRoot} from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
削除済み: ReactDOM.hydrate
ReactDOM.hydrate
は2022年3月(v18.0.0)に非推奨となりました。React 19ではReactDOM.hydrate
を削除し、ReactDOM.hydrateRoot
を使用する必要があります。
// Before
import {hydrate} from 'react-dom';
hydrate(<App />, document.getElementById('root'));
// After
import {hydrateRoot} from 'react-dom/client';
hydrateRoot(document.getElementById('root'), <App />);
削除済み: unmountComponentAtNode
ReactDOM.unmountComponentAtNode
は2022年3月(v18.0.0)に非推奨となりました。React 19では、root.unmount()
を使用する必要があります。
// Before
unmountComponentAtNode(document.getElementById('root'));
// After
root.unmount();
詳細はcreateRoot
とhydrateRoot
のroot.unmount()
を参照してください。
削除済み: ReactDOM.findDOMNode
ReactDOM.findDOMNode
は2018年10月(v16.6.0)に非推奨となりました。
実行速度が遅く、リファクタリングに対して脆弱で、最初の子要素しか返さず、抽象化レベルを壊すため(詳細はこちらを参照)、findDOMNode
を削除します。ReactDOM.findDOMNode
はDOM refsに置き換えることができます。
// Before
import {findDOMNode} from 'react-dom';
function AutoselectingInput() {
useEffect(() => {
const input = findDOMNode(this);
input.select()
}, []);
return <input defaultValue="Hello" />;
}
// After
function AutoselectingInput() {
const ref = useRef(null);
useEffect(() => {
ref.current.select();
}, []);
return <input ref={ref} defaultValue="Hello" />
}
新しい非推奨事項
非推奨: element.ref
React 19 は ref
をプロパティとしてサポートするようになったため、element.ref
は非推奨となり、element.props.ref
を使用するように変更されました。
element.ref
にアクセスすると警告が表示されます。
element.ref
へのアクセスは今後サポートされません。ref
は通常のプロパティになりました。将来のリリースでJSXのElement型から削除されます。非推奨: react-test-renderer
react-test-renderer
は独自のレンダラー環境を実装しており、ユーザーが使用する環境と一致しないため、実装の詳細のテストを促進し、Reactの内部構造の自己検査に依存していることから非推奨とします。
テストレンダラーは、React Testing Libraryのような、より実現可能なテスト戦略が利用できるようになる前に作成されました。現在では、代わりに最新のテストライブラリを使用することをお勧めします。
React 19 では、react-test-renderer
は非推奨警告をログ出力し、コンカレントレンダリングに切り替わりました。最新のテストエクスペリエンスのために、テストを@testing-library/reactまたは@testing-library/react-nativeに移行することをお勧めします。
主な変更点
StrictModeの変更点
React 19 には、StrictMode に対するいくつかの修正と改善が含まれています。
開発時のStrictModeでダブルレンダリングを行う場合、useMemo
と useCallback
は、2回目のレンダリング中に1回目のレンダリングからのメモ化された結果を再利用します。既にStrictModeと互換性のあるコンポーネントでは、動作の違いに気付かないはずです。
すべてのStrictModeの動作と同様に、これらの機能は開発中にコンポーネントのバグを事前に検出できるように設計されているため、本番環境にリリースする前に修正できます。たとえば、開発中は、StrictModeは初期マウント時にrefコールバック関数を2回呼び出して、マウントされたコンポーネントがSuspenseフォールバックに置き換えられた場合に発生することをシミュレートします。
Suspenseの改善点
React 19 では、コンポーネントがサスペンドされると、React は兄弟ツリー全体がレンダリングされるのを待たずに、最も近い Suspense バウンダリのフォールバックをすぐにコミットします。フォールバックのコミット後、React はサスペンドされた兄弟に対する別のレンダリングをスケジュールして、ツリーの残りの部分にある遅延リクエストを「プリウォーム」します。


以前は、コンポーネントがサスペンドされると、サスペンドされた兄弟がレンダリングされてからフォールバックがコミットされていました。


React 19 では、コンポーネントがサスペンドされると、フォールバックがコミットされてから、サスペンドされた兄弟がレンダリングされます。
この変更により、Suspense フォールバックはより速く表示されるようになり、サスペンドされたツリー内の遅延リクエストのウォーミングも継続されます。
UMDビルドの削除
UMD は過去、ビルドステップなしで React をロードする便利な方法として広く使用されていました。現在では、HTML ドキュメントにスクリプトとしてモジュールをロードするための最新の代替手段があります。React 19 以降、React はテストとリリースプロセスの複雑さを軽減するために、UMD ビルドを生成しなくなります。
スクリプトタグを使用して React 19 をロードするには、esm.sh などの ESM ベースの CDN を使用することをお勧めします。
<script type="module">
import React from "https://esm.sh/react@19/?dev"
import ReactDOMClient from "https://esm.sh/react-dom@19/client?dev"
...
</script>
React内部に依存するライブラリはアップグレードを妨げる可能性があります
このリリースには、SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
のような内部構造を使用しないようお願いを無視するライブラリに影響を与える可能性のある React 内部構造の変更が含まれています。これらの変更は、React 19 の改善を実現するために必要であり、ガイドラインに従っているライブラリを破壊することはありません。
当社のバージョン管理ポリシーに基づき、これらの更新は破壊的変更としてリストされておらず、アップグレード方法に関するドキュメントも含まれていません。内部構造に依存するコードを削除することをお勧めします。
内部構造の使用の影響を反映するために、SECRET_INTERNALS
サフィックスの名前を次のように変更しました。
_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE
将来、内部構造へのアクセスをより積極的にブロックして、使用を抑制し、ユーザーがアップグレードを妨げられないようにします。
TypeScriptの変更点
非推奨のTypeScript型を削除しました
React 19で削除されたAPIに基づいて、TypeScriptの型を整理しました。削除された型の一部はより関連性の高いパッケージに移され、その他はReactの動作を記述するのに不要になったものです。
サポートされている置換の一覧については、types-react-codemod
をご覧ください。コードモッドが不足していると思われる場合は、不足しているReact 19コードモッドの一覧で追跡できます。
ref
のクリーンアップが必要
この変更は、react-19
コードモッドプリセットにno-implicit-ref-callback-return
として含まれています。
refクリーンアップ関数の導入により、refコールバックから他の値を返すことはTypeScriptによって拒否されるようになりました。修正は通常、暗黙的な戻り値の使用を中止することです。
- <div ref={current => (instance = current)} />
+ <div ref={current => {instance = current}} />
元のコードはHTMLDivElement
のインスタンスを返し、TypeScriptはこれがクリーンアップ関数であるかどうかを認識できませんでした。
useRef
は引数を必要とします
この変更は、react-19
コードモッドプリセットにrefobject-defaults
として含まれています。
TypeScriptとReactの動作に関する長年の不満はuseRef
でした。 useRef
が引数を必要とするように型を変更しました。これにより、その型シグネチャが大幅に簡素化されます。createContext
のように動作するようになります。
// @ts-expect-error: Expected 1 argument but saw none
useRef();
// Passes
useRef(undefined);
// @ts-expect-error: Expected 1 argument but saw none
createContext();
// Passes
createContext(undefined);
これにより、すべてのrefが変更可能になります。null
で初期化したため、refを変更できないという問題に遭遇することはなくなります。
const ref = useRef<number>(null);
// Cannot assign to 'current' because it is a read-only property
ref.current = 1;
useRef
が常に返す単一のRefObject
型の代わりに、MutableRef
は非推奨になりました。
interface RefObject<T> {
current: T
}
declare function useRef<T>: RefObject<T>
useRef
には、useRef<T>(null)
の便宜的なオーバーロードがあり、RefObject<T | null>
を自動的に返します。useRef
に必要な引数による移行を容易にするために、useRef(undefined)
の便宜的なオーバーロードが追加され、RefObject<T | undefined>
を自動的に返すようになりました。
この変更に関する以前の議論については、[RFC]すべてのrefを変更可能にするをご覧ください。
ReactElement
TypeScript型への変更
この変更は、react-element-default-any-props
コードモッドに含まれています。
React要素のprops
は、要素がReactElement
として型指定されている場合、any
ではなくunknown
をデフォルトで使用するようになりました。 ReactElement
に型引数を渡す場合は、この変更の影響を受けません。
type Example2 = ReactElement<{ id: string }>["props"];
// ^? { id: string }
ただし、デフォルトに依存していた場合は、unknown
を処理する必要があります。
type Example = ReactElement["props"];
// ^? Before, was 'any', now 'unknown'
要素プロパティの不正なアクセスに依存するレガシーコードが多数ある場合にのみ必要です。要素のイントロスペクションは脱出ハッチとしてのみ存在し、明示的なany
を使用して、プロパティへのアクセスが不正であることを明示的にする必要があります。
TypeScriptにおけるJSX名前空間
この変更は、react-19
codemodプリセットにscoped-jsx
として含まれています。
長年の要望として、グローバルなJSX
名前空間を型から削除し、React.JSX
を使用することが挙げられていました。これは、グローバル型の汚染を防ぎ、JSXを利用する異なるUIライブラリ間の競合を防ぐのに役立ちます。
JSX名前空間のモジュール拡張は、`declare module ”…”`でラップする必要があります。
// global.d.ts
+ declare module "react" {
namespace JSX {
interface IntrinsicElements {
"my-element": {
myElementProps: string;
};
}
}
+ }
正確なモジュール指定子は、tsconfig.json
のcompilerOptions
で指定したJSXランタイムによって異なります。
"jsx": "react-jsx"
の場合、react/jsx-runtime
になります。"jsx": "react-jsxdev"
の場合、react/jsx-dev-runtime
になります。"jsx": "react"
と"jsx": "preserve"
の場合、react
になります。
改善されたuseReducer
の型定義
useReducer
は、@mfp22のおかげで、型推論が改善されました。
ただし、これには破壊的変更が必要でした。useReducer
は、型パラメーターとして完全なreducer型を受け付けなくなりました。代わりに、何も受け付けない(コンテキスト型付けに依存する)か、状態とアクションの両方の型を受け付ける必要があります。
新しいベストプラクティスは、useReducer
に型引数を渡さないことです。
- useReducer<React.Reducer<State, Action>>(reducer)
+ useReducer(reducer)
状態とアクションを明示的に型指定し、Action
をタプルで渡すことで、例外的なケースでは機能しない可能性があります。
- useReducer<React.Reducer<State, Action>>(reducer)
+ useReducer<State, [Action]>(reducer)
reducerをインラインで定義する場合は、関数のパラメーターに注釈を付けることをお勧めします。
- useReducer<React.Reducer<State, Action>>((state, action) => state)
+ useReducer((state: State, action: Action) => state)
これは、useReducer
呼び出しの外にreducerを移動した場合にも行う必要があることです。
const reducer = (state: State, action: Action) => state;
変更ログ
その他の破壊的変更
- react-dom:
src
およびhref
におけるJavaScript URLのエラー #26507 - react-dom:
onRecoverableError
からのerrorInfo.digest
の削除 #28222 - react-dom:
unstable_flushControlled
の削除 #26397 - react-dom:
unstable_createEventHandle
の削除 #28271 - react-dom:
unstable_renderSubtreeIntoContainer
の削除 #28271 - react-dom:
unstable_runWithPriority
の削除 #28271 - react-is:
react-is
からの非推奨メソッドの削除 28224
その他の注目すべき変更点
- react: バッチ同期、デフォルトレーンと連続レーン #25700
- react: サスペンドされたコンポーネントの兄弟をプリレンダリングしない #26380
- react: レンダリングフェーズの更新によって発生する無限更新ループを検出する #26625
- react-dom: popstateにおけるトランジションが同期処理になった #26025
- react-dom: SSR中のレイアウト効果に関する警告を削除 #26395
- react-dom: src/hrefに空文字列を設定しないよう警告を表示(アンカータグを除く) #28124
変更点の完全なリストについては、変更ログを参照してください。
この投稿のレビューと編集にご協力いただいたAndrew Clarkさん、Eli Whiteさん、Jack Popeさん、Jan Kassensさん、Josh Storyさん、Matt Carrollさん、Noah Lemenさん、Sophie Alpertさん、そしてSebastian Silbermannさんに感謝いたします。