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.0TypeScriptを使用している場合は、型も更新する必要があります。
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さんに感謝いたします。