React v19 リリース
公開日: 2024-12-05
by The React Team
注: React 19 は安定版になりました!
本投稿は 2024年4月 に公開された React 19 RC の内容からの追加を含みます。
- サスペンドされたツリーの事前ウォーム: Improvements to Suspense を参照
- React DOM の static API: New React DOM Static APIs を参照
日付は安定リリース日に合わせて更新しています。
React v19 は npm で利用可能です。React 19 へのアップグレード手順は React 19 Upgrade Guide にステップバイステップでまとめています。この投稿では React 19 の新機能の概要と採用方法を説明します。
目次
- What’s new in React 19
- Improvements in React 19
- How to upgrade
破壊的変更の一覧は Upgrade Guide を参照してください。
React 19 の新機能
Actions
React アプリでよくあるユースケースは、データを変更(mutation)して、その結果に応じて状態を更新することです。たとえばユーザーが名前を変更するフォームを送信した際に API リクエストを送り、応答を処理します。従来は未了状態(pending)、エラー、楽観的更新(optimistic updates)、順次リクエストなどを手動で扱う必要がありました。例えば useState を使って未了やエラーを管理する例:
function UpdateName({}) {
const [name, setName] = useState("");
const [error, setError] = useState(null);
const [isPending, setIsPending] = useState(false);
const handleSubmit = async () => {
setIsPending(true);
const error = await updateName(name);
setIsPending(false);
if (error) {
setError(error);
return;
}
redirect("/path");
};
return (
<div>
<input value={name} onChange={(event) => setName(event.target.value)} />
<button onClick={handleSubmit} disabled={isPending}>Update</button>
{error && <p>{error}</p>}
</div>
);
}
React 19 では、遷移(transition)内で async 関数を使えるようにして、未了状態・エラー・フォーム・楽観的更新を自動で扱えるようにしています。たとえば useTransition を使って未了状態を処理する例:
function UpdateName({}) {
const [name, setName] = useState("");
const [error, setError] = useState(null);
const [isPending, startTransition] = useTransition();
const handleSubmit = () => {
startTransition(async () => {
const error = await updateName(name);
if (error) {
setError(error);
return;
}
redirect("/path");
})
};
return (
<div>
<input value={name} onChange={(event) => setName(event.target.value)} />
<button onClick={handleSubmit} disabled={isPending}>Update</button>
{error && <p>{error}</p>}
</div>
);
}
この async 遷移は即座に isPending を true にし、非同期リクエストを実行し、遷移が完了して最終ステート更新がコミットされると isPending を false に戻します。これによりデータ変更中も現在の UI を応答可能・操作可能に保てます。
注: async 遷移を使う関数は慣例として “Actions” と呼ばれます。
Actions は以下を自動で管理します:
- Pending state: リクエスト開始時に pending 状態になり、最終的な状態更新がコミットされると自動でリセットされます。
- Optimistic updates: useOptimistic フックとの連携をサポートし、送信中に即時フィードバックを表示できます。
- Error handling: リクエストが失敗したときに Error Boundary を表示したり、楽観的更新を元に戻したりできます。
- Forms: <form> 要素は action と formAction props に関数を渡すことをサポートします。action に関数を渡すとデフォルトで Actions を使い、送信後に自動でフォームをリセットします。
Actions の上に構築された新機能として、React 19 では楽観的更新を管理する useOptimistic と、Actions の一般的なケースを扱うための新フック React.useActionState を導入しています。react-dom ではフォームを自動管理する <form> Actions と、フォーム内での典型的ユースケースをサポートする useFormStatus を追加しました。
前述の例は React 19 では次のように簡略化できます:
function ChangeName({ name, setName }) {
const [error, submitAction, isPending] = useActionState(async (previousState, formData) => {
const error = await updateName(formData.get("name"));
if (error) {
return error;
}
redirect("/path");
return null;
}, null);
return (
<form action={submitAction}>
<input type="text" name="name" />
<button type="submit" disabled={isPending}>Update</button>
{error && <p>{error}</p>}
</form>
);
}
次のセクションで React 19 の Actions の各機能を詳しく説明します。
新しいフック: useActionState
Actions の一般的なケースを簡単にするため、useActionState という新しいフックを追加しました:
const [error, submitAction, isPending] = useActionState(async (previousState, newName) => {
const error = await updateName(newName);
if (error) {
return error;
}
return null;
}, null);
useActionState は関数(“Action”)を受け取り、呼び出すためのラップされた Action を返します。これは Action が合成可能であるため機能します。ラップされた Action が呼ばれると、useActionState は Action の最後の結果を data として、Action の pending 状態を pending として返します。
注: React.useActionState は Canary リリースでは以前 ReactDOM.useFormState と呼ばれていましたが、名前を変更し useFormState を非推奨にしました。詳細は #28491 を参照してください。
詳しくは useActionState のドキュメントを参照してください。
React DOM: <form> Actions
Actions は react-dom の新しい <form> 機能とも統合されています。<form>, <input>, <button> の action と formAction props に関数を渡すことで、Actions を使ってフォームを自動送信できます:
<form action={actionFunction}>
<form> Action が成功すると、React はアンコントロールコンポーネント(uncontrolled components)のフォームを自動でリセットします。手動でリセットする必要がある場合は、新しい requestFormReset React DOM API を呼び出せます。詳細は react-dom の <form>, <input>, <button> のドキュメントを参照してください。
React DOM: 新しいフック useFormStatus
デザインシステムでは、コンポーネントにフォームの状態情報を渡すために props を深く渡さずに済ませたいことがよくあります。Context を使えば可能ですが、一般的なケースを簡単にするために新しいフック useFormStatus を追加しました:
import { useFormStatus } from 'react-dom';
function DesignButton() {
const { pending } = useFormStatus();
return <button type="submit" disabled={pending} />;
}
useFormStatus は親 <form> の状態をあたかも Context プロバイダのように読み取ります。詳細は react-dom の useFormStatus のドキュメントを参照してください。
新しいフック: useOptimistic
データの変更中に最終状態を楽観的に表示するパターンはよくあります。React 19 ではこれを簡単にする useOptimistic を追加しました:
function ChangeName({ currentName, onUpdateName }) {
const [optimisticName, setOptimisticName] = useOptimistic(currentName);
const submitAction = async formData => {
const newName = formData.get("name");
setOptimisticName(newName);
const updatedName = await updateName(newName);
onUpdateName(updatedName);
};
return (
<form action={submitAction}>
<p>Your name is: {optimisticName}</p>
<p>
<label>Change Name:</label>
<input type="text" name="name" disabled={currentName !== optimisticName} />
</p>
</form>
);
}
useOptimistic は updateName リクエストが完了するまで optimisticName を即座にレンダリングします。更新が成功・失敗すると React は自動的に currentName に戻します。詳細は useOptimistic のドキュメントを参照してください。
新しい API: use
React 19 では、レンダー内でリソースを読み取るための新しい API use を導入します。例えば Promise を use で読み取ると、Promise が解決されるまで React がサスペンドします:
import { use } from 'react';
function Comments({ commentsPromise }) {
const comments = use(commentsPromise);
return comments.map(comment => <p key={comment.id}>{comment}</p>);
}
function Page({ commentsPromise }) {
return (
<Suspense fallback={<div>Loading...</div>}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
);
}
注意: use は render 内で作られた Promise をサポートしません。render 内で作成された Promise を use に渡すと、次のような警告が出ます:
Console
A component was suspended by an uncached promise. Creating promises inside a Client Component or hook is not yet supported, except via a Suspense-compatible library or framework.
修正するには、Promise のキャッシュをサポートする Suspense 対応のライブラリ/フレームワークから渡される Promise を use に渡す必要があります。将来的には render 内で Promise をキャッシュしやすくする機能を追加する予定です。
また use を使って Context を読み取ることもでき、早期 return の後など条件付きで Context を取得できます:
import { use } from 'react';
import ThemeContext from './ThemeContext';
function Heading({ children }) {
if (children == null) {
return null;
}
const theme = use(ThemeContext);
return <h1 style={{ color: theme.color }}>{children}</h1>;
}
use API はフックと同様にレンダーでのみ呼び出せます。フックとは異なり、条件付きで呼び出すことが可能です。将来的に use でレンダー中にリソースを消費するためのより多くの方法をサポートする予定です。詳細は use のドキュメントを参照してください。
新しい React DOM Static APIs
静的サイト生成のために react-dom/static に 2 つの新しい API を追加しました:
- prerender
- prerenderToNodeStream
これらは renderToString を改良したもので、静的 HTML 生成時にデータの読み込みを待機します。Node.js Streams や Web Streams のようなストリーミング環境で動作するように設計されています。たとえば Web Stream 環境では prerender を使って React ツリーを静的 HTML にプリレンダリングできます:
import { prerender } from 'react-dom/static';
async function handler(request) {
const { prelude } = await prerender(<App />, { bootstrapScripts: ['/main.js'] });
return new Response(prelude, { headers: { 'content-type': 'text/html' } });
}
Prerender API は静的な HTML を返す前にすべてのデータ読み込みを待ちます。ストリームは文字列に変換することも、ストリーミングレスポンスとして送ることも可能です。これらは読み込みに合わせて段階的にストリーミングするのではなく、データを待ってから静的 HTML を生成します。ストリーミングを段階的に行いたい場合は既存の React DOM server rendering APIs を利用してください。詳細は React DOM Static APIs を参照してください。
React Server Components
Server Components は、クライアントアプリや SSR サーバーとは別の環境で、バンドル前にコンポーネントを先にレンダリングできる新しいオプションです。この別環境が React Server Components における “サーバー” です。Server Components は CI サーバー上でビルド時に一度だけ実行することも、ウェブサーバーで各リクエストごとに実行することもできます。
React 19 には Canary チャンネルから提供されていた Server Components の機能がすべて含まれています。つまり Server Compone
(注: ソースはここで途中で切れています。続きは公式ドキュメントを参照してください。)