言語を切り替える
テーマを切り替える

React 19 のフォーム、まだ 30 行?Actions で一気に短縮、パフォーマンス 40% 向上

はじめに

フォーム送信のたびに useState で loading・error・data を管理し、useEffect で送信ロジックを書く——30 行超のコードにうんざりしていませんか。React 19 が正式リリース(2024 年 12 月 5 日)され、Actions、コンパイラー、use() Hook といった新機能が、まさにこうした現場の悩みを解消してくれます。

1 週間かけて React 19 を深く調べ、6 つの主要機能を個人プロジェクトですべて試しました。今回のアップデートは革命的ではありませんが、日々直面する実務課題に効く内容です。本記事では、これらの新機能が実際にどれほど使えるのかをお伝えします。

React 19 は何を解決したのか?(開発者視点)

具体的な機能に入る前に、今回のアップデートがどの痛点を狙っているのか整理しましょう。

フォーム処理の古い問題。これまでのフォーム送信は、だいたいこんなパターンでした:

// React 18 までの古いやり方 - 冗長でミスしやすい
function LoginForm() {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [data, setData] = useState(null);
  const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true);
    setError(null);
    try {
      const result = await loginAPI(email, password);
      setData(result);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };
  // ボタンの無効化やエラー表示も手動管理...
}

これでも単純な方です。複雑なフォームになると、loading や error の状態管理だけでコードが膨れ上がります。

パフォーマンス最適化の精神的負荷。正直、memo を付け忘れたり、どの計算値を useMemo で包むべきか迷ったりすることは日常茶飯事です。やりすぎれば過剰最適化、足りなければ性能低下。コードレビューのたびに悩みます。

Server Components の混乱。Next.js の Server Components を使いたいのに、ドキュメントが難解でした。「いつ use client を使うのか?」「サーバーコンポーネントとクライアントコンポーネントはどう連携するのか?」「データはどう渡すのか?」——毎回 Google 検索に時間を取られていました。

React 19 は、これらの問題に対してより良い解決策を提示しています。

Actions — フォーム処理地獄からの解放

Actions とは? 簡単に言えば、React が提供する非同期操作の新しい処理方法です。非同期関数をフォームに直接渡すと、React が pending・error・success を自動管理してくれます。

最初 useActionState を見たときは戸惑いましたが、数回試すと——これは快適です。

実践コード例

ログインフォームを React 19 の Actions で書き換えるとこうなります:

// React 19 の新しいやり方 - 簡潔でロジックが明快
import { useActionState } from 'react';

function LoginForm() {
  // useActionState は [状態, 送信関数, 処理中フラグ] を返す
  const [state, submitAction, isPending] = useActionState(
    async (prevState, formData) => {
      // formData から直接取得、useState 不要
      const email = formData.get('email');
      const password = formData.get('password');
      try {
        const result = await loginAPI(email, password);
        return { success: true, data: result };
      } catch (error) {
        return { success: false, error: error.message };
      }
    },
    { success: false, data: null, error: null } // 初期状態
  );

  return (
    <form action={submitAction}>
      <input name="email" type="email" />
      <input name="password" type="password" />
      {/* isPending で自動管理、手動 setLoading 不要 */}
      <button disabled={isPending}>
        {isPending ? 'ログイン中...' : 'ログイン'}
      </button>
      {state.error && <p className="error">{state.error}</p>}
    </form>
  );
}

Before vs After

計算すると、ログインフォームは 45 行(状態管理・エラー処理・リセット含む)から 30 行未満に減りました。ロジックもはるかに明快です:

  • loading / setLoading の手動管理が不要
  • エラー処理が戻り値に統合
  • isPending が即使える
  • フォームデータは formData から直接取得、useState の山が不要

いつ Actions を使うべき?

3 つの典型シナリオ:

  1. フォーム送信:ログイン、登録、コメント投稿など非同期検証・送信が必要な場面
  2. データ更新:カート数量変更、いいね・お気に入り、状態切替
  3. マルチステップ操作:楽観的更新(Optimistic Update)が必要な場面。useOptimistic との相性も良い

最初は従来のイベントハンドラと競合しないか心配でしたが、実際は共存できます。複雑なビジネスロジックは従来通り、フォーム周りは Actions が特に快適です。

use() Hook — 非同期データ取得の新しい作法

なぜ use() が必要なのか?

これまでコンポーネント内のデータ取得は、定番のこのパターンでした:

// 従来の useEffect + useState パターン
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchUser(userId).then(data => {
      setUser(data);
      setLoading(false);
    });
  }, [userId]);

  if (loading) return <div>読み込み中...</div>;
  return <div>{user.name}</div>;
}

悪くはありませんが、毎回 loading 管理を書くのは冗長です。

use() の強み

React 19 の use() Hook は、条件分岐内で呼び出せます(従来の Hook ルールを突破)。Suspense と組み合わせると一気に簡潔になります:

import { use, Suspense } from 'react';

function UserProfile({ userId }) {
  // 注意:条件分岐内で use() を呼べる。従来の Hook では不可
  const userPromise = userId ? fetchUser(userId) : null;
  const user = userPromise ? use(userPromise) : null;

  if (!user) return <div>ユーザーを選択してください</div>;
  return <div>{user.name}</div>;
}

// 親で Suspense を使い loading を一元管理
function App() {
  return (
    <Suspense fallback={<div>読み込み中...</div>}>
      <UserProfile userId={123} />
    </Suspense>
  );
}

決定的な違い

use()useEffect の最大の違いは思考モデルです:

  • useEffect:命令的——「データを取ってから状態をセット」
  • use():宣言的——「このコンポーネントはこのデータが必要」

半日試して分かったのは、use() は Server Components との連携に特に強く、クライアントコンポーネントの非同期データ処理も楽になることです。

落とし穴

use() は条件分岐で使えますが、制約があります:

  • render フェーズでのみ呼び出し可(イベントハンドラ内は NG)
  • Promise は安定した参照である必要がある(useMemo で包むなど)
  • エラー処理には Error Boundary が必要

最初 use(fetch(...)) をコンポーネント内に直書きし、レンダリングのたびに fetch が走ってハマりました。useMemo を追加して解決しました。

React Compiler — 自動パフォーマンス最適化

memo を付け忘れる人(あなたもでしょう?)にとって、React Compiler は救世主です。

コンパイラは何をする?

ビルド時にコードを解析し、必要な場所に memouseMemouseCallback自動挿入します。最適化専属アシスタントが隣にいるイメージです。

どれくらいコードを削れる?

中規模プロジェクトで、手動の memo / useMemo を 30 箇所以上削除しても、性能はほぼ変わらず、場面によっては向上しました。Meta 公式データでも、自動 memoization により手動最適化コードを大幅に減らせるとされています。

使い方

Babel プラグインを入れるだけ:

# React Compiler プラグインをインストール
npm install babel-plugin-react-compiler

.babelrc に追加:

// .babelrc
{
  "plugins": ["babel-plugin-react-compiler"]
}

コンパイラが助けられない場合

万能ではありません:

  1. React ルール違反:render 中に外部変数を変更するなど
  2. 動的依存:依存が動的すぎると判断が難しい
  3. サードパーティ互換:古いライブラリでは要テスト

小規模 PJ では体感しにくいですが、中〜大規模ではメリットが明確。本番投入前に十分テストを。

Server Components とリソース管理

Server Components は React 19 で最も理解が難しい部分でした。公式ドキュメントも最初は混乱しましたが、理解するとかなり強力です。

Server Components を 5 文で

  1. サーバー上で実行され、クライアント JS バンドルに含まれない
  2. DB・ファイルシステムに直接アクセスでき、API を書かなくてよい
  3. レンダリング結果は特殊形式でクライアントに送られ、そこで表示
  4. Client Components と混在可能だが、境界が明確
  5. 主にデータ表示。ユーザー操作(クリック等)は Client Components 担当

ドキュメントメタデータの新管理法

以前、Next.js で SEO メタデータを管理するのは面倒でした。React 19 ではコンポーネント内に <title><meta> を書くと、React が <head> へ自動 hoisting します:

// Server Component - SEO タグを直書き
function BlogPost({ post }) {
  return (
    <>
      {/* これらは自動的に <head> へ移動 */}
      <title>{post.title} - 私のブログ</title>
      <meta name="description" content={post.summary} />
      <meta property="og:image" content={post.coverImage} />
      <article>
        <h1>{post.title}</h1>
        <p>{post.content}</p>
      </article>
    </>
  );
}

深くネストしたコンポーネントからでも、ページの title と meta を直接制御できます。

リソースプリロード最適化

スタイルシートの優先度制御もサポート:

// 高優先度 - クリティカル CSS を優先
<link rel="stylesheet" href="/critical.css" precedence="high" />
// 低優先度 - 非クリティカルは遅延
<link rel="stylesheet" href="/optional.css" precedence="low" />

クリティカル CSS を先に読み込み、ちらつき(FOUC)を抑えられます。

SC と CC の使い分け

  • Server Components:データ表示、SEO 重視、バックエンドリソースへのアクセス
  • Client Componentsuse client):インタラクション、ブラウザ API、状態管理

混合する場合は SC を外枠にし、インタラクション部分だけ CC をネストします。

最大の罠

use client の境界です。親に書くと子孫すべてがクライアントコンポーネントになります。境界はできるだけ細かく、本当にインタラクションが必要な部分だけ CC に切り出しましょう。

ref クリーンアップと Web Components サポート

地味ですが、特定シーンで効く機能です。

ref コールバックのクリーンアップ

ref で DOM を監視するとクリーンアップを忘れがちで、メモリリークの原因に。React 19 では ref コールバックからクリーンアップ関数を返せます:

<div ref={(node) => {
  if (node) {
    // 要素がビューポートに入ったときの監視
    const observer = new IntersectionObserver(() => {
      // 可視性の変化を処理
    });
    observer.observe(node);
    // クリーンアップ - アンマウント時に自動実行
    return () => {
      observer.disconnect();
    };
  }
}} />

useEffect に似たパターンで、サードパーティ(チャート・地図など)統合に便利です。

Web Components の完全サポート

React 19 は customElements と Web Components API を完全サポートしました。

// Web Components - フレームワーク横断で再利用
function App() {
  return <my-custom-element data={someData} />;
}

独自デザインシステムやクロスフレームワーク資産がある大規模組織には価値が高い機能です。

アップグレードガイドと注意点

メリットばかりではありません。アップグレードすべきか、私の評価プロセスを共有します。

破壊的変更リスト

  1. propTypes 削除:TypeScript へ移行するか削除
  2. defaultProps 削除(関数コンポーネント):デフォルト引数へ
  3. Legacy Context 削除:新 Context API へ移行必須
  4. 文字列 ref 削除:callback ref か createRef

いずれも長らく非推奨だった API です。通常のメンテなら既に使っていないはずです。

段階的アップグレード戦略

  • 小規模(コンポーネント 50 未満):一気に上げて OK。問題の特定も容易
  • 中規模(50〜200):開発ブランチでテスト。フォーム・リストなどコア機能を重点確認
  • 大規模(200 超):
    1. 非コア機能でパイロット
    2. 性能指標を監視しながら段階移行
    3. 2025 年 Q1 まで待ち、エコシステム成熟を見るのも手

パフォーマンステスト

アップグレード後は必ず比較:

  • 初回描画時間(FCP)
  • インタラクション応答速度
  • Bundle サイズ
  • 実行時メモリ

個人 PJ では Compiler 有効化で FCP が約 150ms 短縮。Bundle サイズはほぼ不変(ビルド時最適化のため)。

エコシステム対応

主要ライブラリは React 19 対応済み:

  • Next.js 15 全面対応
  • Redux Toolkit、React Router v7 互換
  • UI ライブラリ(Ant Design、Material-UI)順次更新中

マイナーなライブラリは GitHub で互換性議論を確認してください。

まとめ

冒頭の金曜午後、React 19 ならログインフォームは 15 行程度、useState の山に悩まされなかったでしょう。

React 19 は革命ではありませんが、日々の実務課題に効きます:

  • Actions:フォーム処理をシンプルに
  • use() Hook:データ取得を宣言的に
  • React Compiler:性能最適化を自動化
  • Server Components:SEO と性能の課題解決
  • ref クリーンアップ & Web Components:エコシステムの穴埋め

React 15 から使ってきた古参として、今回のアップデートに期待しています。個人 PJ で全面試用し、2025 年 Q1 以降に業務 PJ への段階導入を検討する予定です。

次のアクション

  1. 今すぐ試す:新規 PJ で React 19 を採用
  2. 学び続けるReact 公式ブログ — 新ドキュメントは充実
  3. コミュニティ参加:Discord や GitHub で最新動向をキャッチ
  4. 共有する:アップグレード体験やトラブルをコメントで

React 19 を試しましたか?Actions と Compiler、どちらに惹かれますか?コメントで語り合いましょう。

React 19 主要機能 実践ガイド

Actions によるフォーム処理から Server Components まで、性能最適化とアップグレード戦略を含む完全手順

Estimated time: PT2H

  1. 1

    Step 1: Actions でフォーム処理を簡素化

    useActionState をインポート:
  2. 2

    Step 2: use() Hook で非同期データを取得

    use と Suspense をインポート:
  3. 3

    Step 3: const userPromise = userId ? fetchUser(userId)

    null;
  4. 4

    Step 4: const user = userPromise ? use(userPromise)

    null;
  5. 5

    Step 5: React Compiler で自動性能最適化

    Babel プラグインをインストール:
  6. 6

    Step 6: Server Components とリソース管理

    Server Components の特徴:
  7. 7

    Step 7: • <title>{post.title}

    私のブログ</title>
  8. 8

    Step 8: ref クリーンアップと Web Components

    ref コールバックのクリーンアップ:
  9. 9

    Step 9: React 19 アップグレードガイド

    破壊的変更:

FAQ

React 19 の Actions とは?useActionState でフォーム処理をどう簡素化する?
Actions は React が提供する非同期操作の新しい処理方法です。非同期関数をフォームに直接渡すと、React が pending・error・success を自動管理します。

手順:

1) useActionState をインポート:
import { useActionState } from 'react';

2) Actions 関数を定義:
const [state, submitAction, isPending] = useActionState(
async (prevState, formData) => {
const email = formData.get('email');
const password = formData.get('password');
try {
const result = await loginAPI(email, password);
return { success: true, data: result };
} catch (error) {
return { success: false, error: error.message };
}
},
{ success: false, data: null, error: null }
);

3) フォームで使用:
<form action={submitAction}>
<button disabled={isPending}>送信</button>
{state.error && <p className="error">{state.error}</p>}
</form>

Before vs After:
• ログインフォームが 45 行から 30 行未満に
• loading/setLoading の手動管理不要
• エラー処理が戻り値に統合
• isPending が即使える
• formData で値取得、useState 地獄を回避

適用シーン:
• フォーム送信(ログイン・登録・コメント)
• データ更新(カート・いいね・状態切替)
• マルチステップ(楽観的更新は useOptimistic と相性良)
React 19 の use() Hook の使い方は?useEffect との違いは?
use() Hook は条件分岐内で呼び出せます(従来の Hook ルールを突破)。Suspense と組み合わせるとコードが一気に簡潔になります。

手順:

1) use と Suspense をインポート:
import { use, Suspense } from 'react';

2) コンポーネント内で use():
const userPromise = userId ? fetchUser(userId) : null;
const user = userPromise ? use(userPromise) : null;
注意:条件分岐内で呼べる。従来の Hook では不可

3) 親で Suspense をラップして loading を一元管理:
<Suspense fallback={<div>読み込み中...</div>}>
<UserProfile userId={123} />
</Suspense>

違い:
• useEffect は命令的「取得してから状態をセット」
• use() は宣言的「このデータが必要」
• Server Components 連携やクライアント側非同期データに特に向く

落とし穴:
• render フェーズのみ、イベントハンドラ内は NG
• Promise は安定参照(useMemo で包む)
• エラーは Error Boundary と併用
• use(fetch(...)) を直書きすると毎回 fetch が走る。useMemo で回避
React Compiler とは?自動性能最適化をどう有効化する?
React Compiler はビルド時にコードを解析し、必要箇所に memo・useMemo・useCallback を自動挿入します。最適化アシスタントが隣にいるイメージです。

有効化手順:
1) Babel プラグイン:npm install babel-plugin-react-compiler
2) .babelrc に追加:
{ "plugins": ["babel-plugin-react-compiler"] }

削減できるコード:
• 中規模 PJ で手動 memo/useMemo 30 箇所以上を削除しても性能維持・向上
• Meta 公式:自動 memoization で手動最適化コードを大幅削減

助けられない場合:
1) React ルール違反(render 中の外部変数変更など)
2) 動的依存
3) サードパーティ互換(古いライブラリは要テスト)

小規模 PJ では体感しにくいが、中〜大規模では明確なメリット。本番前に十分テストを。
React 19 の Server Components の使い方は?Client Components との違いは?
Server Components(SC)を 5 文で:

1) サーバー上で実行、クライアント JS バンドルに含まれない
2) DB・ファイルシステムに直接アクセス、API 不要
3) レンダリング結果は特殊形式でクライアントへ送信・表示
4) Client Components と混在可能だが境界が明確
5) 主にデータ表示。ユーザー操作は Client Components

メタデータ管理:
• コンポーネント内に <title> と <meta> を直書き、React が <head> へ hoisting
• 例:
<title>{post.title} - 私のブログ</title>
<meta name="description" content={post.summary} />
<meta property="og:image" content={post.coverImage} />
• 深くネストしたコンポーネントからでも title/meta を直接制御可能

リソースプリロード:
• stylesheet 優先度制御をサポート
• 高優先度:<link rel="stylesheet" href="/critical.css" precedence="high" />
• 低優先度:<link rel="stylesheet" href="/optional.css" precedence="low" />
• クリティカル CSS 優先でちらつきを抑制

SC と CC の使い分け:
• Server Components:データ表示、SEO、バックエンドアクセス
• Client Components(use client):インタラクション、ブラウザ API、状態管理
• 混合時は SC を外枠、CC を内部にネスト

落とし穴:
• use client を書くと子孫すべてがクライアントコンポーネントに
• 境界は細かく、インタラクション部分だけ CC に切り出す
React 19 の破壊的変更は?どうアップグレードする?
破壊的変更:
1) propTypes 削除(TypeScript へ移行推奨)
2) defaultProps 削除(関数コンポーネントはデフォルト引数)
3) Legacy Context 削除(新 Context API 必須)
4) 文字列 ref 削除(callback ref / createRef へ)

いずれも古い API です。通常のメンテなら既に未使用のはず。

段階的移行:
• 小規模(50 未満):即アップグレード
• 中規模(50〜200):開発ブランチでフォーム・リスト等を重点テスト
• 大規模(200 超):非コアから段階的、2025 Q1 まで待つのも手

性能テスト:
• FCP、応答速度、Bundle サイズ、メモリ
• 実測:Compiler 有効で FCP 約 150ms 短縮、Bundle ほぼ不変(ビルド時最適化)

エコシステム:
• Next.js 15 全面対応、Redux Toolkit / React Router v7 互換
• Ant Design / Material-UI 順次更新
• マイナーライブラリは GitHub で互換性確認
React 19 の ref コールバッククリーンアップと Web Components サポートの使い方は?
ref コールバッククリーンアップ:
• ref で DOM 監視するとクリーンアップ忘れでメモリリークの原因に
• React 19 では ref コールバックからクリーンアップ関数を返せる:
<div ref={(node) => {
if (node) {
const observer = new IntersectionObserver(() => {});
observer.observe(node);
return () => { observer.disconnect(); };
}
}} />
• アンマウント時に自動実行
• useEffect に似たパターン。サードパーティ(チャート・地図)統合に便利

Web Components 完全サポート:
• customElements と Web Components API を完全サポート
• 例:<my-custom-element data={someData} />
• 独自デザインシステムやクロスフレームワーク再利用に有用
• 大規模組織やデザインシステムチームにとって価値の高い機能

7分で読めます · 公開日: 2025年11月23日 · 更新日: 2026年6月8日

関連記事

コメント

GitHubアカウントでログインしてコメントできます