参考: React 19 公式ブログ
React 19 以前のアップデート振り返り
React 16.8(2019)— Hooks の登場
useStateuseEffectuseContextuseReducer
→ React 開発の基礎ができた
React 18(2022)— 「非同期 UI」がテーマ
- Concurrent Rendering
useTransitionuseDeferredValue- Suspense 強化
- Streaming SSR
React 19 の概要
フォームを送信して API を叩き、結果で state を更新する処理は React アプリの定番パターン。
これまでは「送信中のローディング表示」「エラーハンドリング」「レスポンスに応じた state 更新」「リクエストの順序管理」などを全部自分で書く必要がありましたが、React 19 では 非同期処理をトランジションの中に書くだけで、これらを自動でやってくれるようになります。
追加機能一覧
Actions
これまで
onSubmit
↓
fetch
↓
loading 管理
↓
error 管理
↓
state 更新
React 19 では
<form action={action}>
action に関数を渡すだけで処理できます。
useActionState
Action の状態を管理するための Hook
const [state, formAction, isPending] = useActionState(actionFn, 初期値);
| 戻り値 | 説明 |
|---|---|
state |
Action 関数の戻り値(現在の状態) |
formAction |
form の action に渡す関数 |
isPending |
Action 実行中かどうか(boolean) |
第一引数のアクション関数の戻り値が、そのまま state に反映されます。
コード例
"use client";
import { useActionState } from "react";
type State = {
message: string;
};
async function submitForm(prevState: State, formData: FormData): Promise<State> {
await new Promise((resolve) => setTimeout(resolve, 2000));
const name = formData.get("name");
return { message: `${name} さんの送信が完了しました` };
}
export default function Page() {
const [state, formAction, isPending] = useActionState(submitForm, { message: "" });
return (
<form action={formAction}>
<input name="name" placeholder="名前" />
<button disabled={isPending}>
{isPending ? "送信中..." : "送信"}
</button>
<p>{state.message}</p>
</form>
);
}
useActionState = Action の状態を管理する Hook
useFormStatus
親 <form> の送信状態を、子コンポーネントが直接読み取れる Hook
React 18 では子コンポーネントに pending を props で渡すバケツリレーが必要でした。useFormStatus は Context のような仕組みで、これを不要にします。
const { pending, data, method, action } = useFormStatus();
| プロパティ | 説明 |
|---|---|
pending |
送信中かどうか(boolean) |
data |
送信中のみ取得できる FormData |
method |
form の method("get" / "post" など) |
action |
現在実行中の action 関数 |
⚠️ 重要な制約: 必ず
<form>タグの「子コンポーネント」内で使うこと
コード例
"use client";
import { useFormStatus } from "react-dom";
function SubmitButton() {
const { pending, data, method, action } = useFormStatus();
return (
<div>
<button disabled={pending}>
{pending ? "送信中..." : "送信"}
</button>
<p>入力値: {data?.get("name")?.toString()}</p>
<p>method: {method}</p>
<p>action: {action?.name}</p>
</div>
);
}
async function submitForm(formData: FormData) {
await new Promise((resolve) => setTimeout(resolve, 3000));
console.log(formData.get("name"));
}
export default function Page() {
return (
<form action={submitForm} method="post">
<input name="name" placeholder="名前" />
<SubmitButton />
</form>
);
}
useFormStatus = 親 form の送信状態を子コンポーネントで props なしに読める Hook
useOptimistic
サーバーの返事を待たずに、先に画面だけ更新する Hook(Optimistic = 楽観的)
const [optimisticState, addOptimisticUpdate] = useOptimistic(state, updateFn);
| 戻り値 | 説明 |
|---|---|
optimisticState |
今画面に見せている状態(本物 + 仮の更新) |
addOptimisticUpdate |
先に見せたい更新をかける関数 |
通常のフロー
ボタン押下 → API 通信 → 成功 → 画面更新
useOptimistic を使ったフロー
ボタン押下 → 先に画面更新 → API 通信 → 成功
いいね・コメント投稿・チャット送信などに向いています。
コード例
"use client";
import { useOptimistic, useState } from "react";
export default function Page() {
const [likes, setLikes] = useState(0);
const [optimisticLikes, addOptimisticLike] = useOptimistic(
likes,
(currentLikes) => currentLikes + 1
);
async function likeAction() {
addOptimisticLike();
await new Promise((resolve) => setTimeout(resolve, 2000));
setLikes((prev) => prev + 1);
}
return (
<div>
<p>いいね数: {optimisticLikes}</p>
<button onClick={likeAction}>いいね</button>
</div>
);
}
useOptimistic = サーバー処理の完了を待たずに、先に UI を更新するための Hook
use()
Promise や Context の値を読み取るための API
const data = use(promise);
これまで
useEffect → fetch → useState に保存 → 画面表示
React 19 では
Suspense と組み合わせることで Promise の結果を直接扱えます。
コード例
import { use, Suspense } from "react";
const fetchUser = async () => {
await new Promise((resolve) => setTimeout(resolve, 2000));
return { name: "Luke" };
};
const userPromise = fetchUser();
function User() {
const user = use(userPromise);
return <p>{user.name}</p>;
}
export default function Page() {
return (
<Suspense fallback={<p>読み込み中...</p>}>
<User />
</Suspense>
);
}
Promise が完了するまでは Suspense の fallback が表示されます。
use() = Promise の結果をコンポーネント内で直接読み取る API
ref as Props
ref を通常の props のように渡せる機能
React 19 以前
子コンポーネントに ref を渡す場合、forwardRef が必要でした。
const Input = forwardRef((props, ref) => {
return <input ref={ref} />;
});
React 19 では
function Input({ ref }) {
return <input ref={ref} placeholder="名前" />;
}
forwardRef なしで ref を渡せます。
コード例
"use client";
import { useRef } from "react";
function Input({ ref }) {
return <input ref={ref} placeholder="名前" />;
}
export default function Page() {
const inputRef = useRef(null);
function focusInput() {
inputRef.current?.focus();
}
return (
<div>
<Input ref={inputRef} />
<button onClick={focusInput}>input にフォーカス</button>
</div>
);
}
ref as Props = ref を通常の props のように扱える機能
Context Provider 簡略化
React 19 以前
<ThemeContext.Provider value={theme}>
<App />
</ThemeContext.Provider>
React 19 では
<ThemeContext value={theme}>
<App />
</ThemeContext>
.Provider を書かなくても Context を渡せるようになりました。
コード例
import { createContext, useContext } from "react";
const ThemeContext = createContext("light");
function Child() {
const theme = useContext(ThemeContext);
return <p>現在のテーマ: {theme}</p>;
}
export default function Page() {
return (
<ThemeContext value="dark">
<Child />
</ThemeContext>
);
}
Context Provider 簡略化 =
<Context.Provider>の代わりに<Context>と書ける機能
まとめ
| 機能 | 概要 |
|---|---|
| Actions | フォーム送信や非同期処理を扱いやすくする仕組み |
| useActionState | Action の実行結果を state として管理する Hook |
| useFormStatus | 親 form の送信状態を子コンポーネントで参照する Hook |
| useOptimistic | サーバー処理を待たずに先に UI を更新する Hook |
| use() | Promise の結果をコンポーネント内で直接読み取る API |
| ref as Props | ref を通常の props のように渡せる機能 |
| Context Provider 簡略化 |
<Context.Provider> → <Context> と書けるようになった |
React 19 は、フォーム・非同期処理・状態管理・Context・ref 周りをよりシンプルに書けるようにしたアップデートです。