React 19 新機能完全ガイド:Actions、use()、Server Components、コンパイラ最適化

前端工程(更新: 2026年5月6日)

React 19:フレームワークレベルのパラダイムアップグレード

React 19 は単なる API 更新ではなく、「ライブラリ」から「フルスタックフレームワーク」への位置づけの転換です。Server Components、Actions、Compiler の 3 大機能が React アプリケーションの書き方を再定義します。

機能 解決する問題 影響範囲
Form Actions フォーム送信のボイラープレートコード すべてのフォーム操作
use() フック 条件付きデータ読み込み データ取得パターン
Server Components クライアントバンドルサイズ アーキテクチャ全体
React Compiler 手動 memo 最適化 パフォーマンス
ref as prop forwardRef ボイラープレート コンポーネント API
Document Metadata SEO/head 管理 SSR アプリケーション

1. Form Actions:フォームボイラープレートにさようなら

従来の書き方

function LoginForm() {
  const [pending, setPending] = useState(false);
  const [error, setError] = useState(null);

  async function handleSubmit(e) {
    e.preventDefault();
    setPending(true);
    setError(null);
    try {
      const formData = new FormData(e.target);
      await login(formData.get('email'), formData.get('password'));
    } catch (err) {
      setError(err.message);
    } finally {
      setPending(false);
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <input name="email" type="email" />
      <input name="password" type="password" />
      <button disabled={pending}>{pending ? 'ログイン中...' : 'ログイン'}</button>
      {error && <p>{error}</p>}
    </form>
  );
}

React 19 の書き方

async function loginAction(formData) {
  // サーバー上で直接実行、手動の pending/error 管理不要
  await login(formData.get('email'), formData.get('password'));
}

function LoginForm() {
  return (
    <form action={loginAction}>
      <input name="email" type="email" />
      <input name="password" type="password" />
      <SubmitButton />
    </form>
  );
}

// useActionStatus が送信状態を自動追跡
function SubmitButton() {
  const { pending } = useActionStatus();
  return <button disabled={pending}>{pending ? 'ログイン中...' : 'ログイン'}</button>;
}

コード削減:約 60%。onSubmitpreventDefault、手動の pending/error 状態が不要に。

useActionState:フォーム状態の管理

function LoginForm() {
  const [state, formAction, isPending] = useActionState(
    async (prevState, formData) => {
      try {
        await login(formData.get('email'), formData.get('password'));
        return { success: true };
      } catch (err) {
        return { error: err.message };
      }
    },
    null
  );

  return (
    <form action={formAction}>
      <input name="email" type="email" />
      <input name="password" type="password" />
      <button disabled={isPending}>ログイン</button>
      {state?.error && <p className="error">{state.error}</p>}
    </form>
  );
}

2. use() フック:条件付きデータ読み込み

use() は React 初の条件文やループ内で呼び出せるフックです。

Promise の読み取り

function UserProfile({ userPromise }) {
  // 条件文内で使用可能!
  if (showProfile) {
    const user = use(userPromise);
    return <h1>{user.name}</h1>;
  }
  return null;
}

Context の読み取り

function ThemeToggle() {
  // 以前は useContext が必要、今は use() を使用可能
  const theme = use(ThemeContext);
  return <button onClick={toggle}>{theme}</button>;
}

Suspense 統合

function UserPage({ userPromise }) {
  return (
    <Suspense fallback={<Spinner />}>
      <UserProfile userPromise={userPromise} />
    </Suspense>
  );
}

function UserProfile({ userPromise }) {
  const user = use(userPromise);
  return <div>{user.name}</div>;
}

3. Server Components 正式安定版

Server と Client コンポーネント

// Server Component(デフォルト)
// - サーバーでレンダリング、JS をクライアントに送信しない
// - データベース、ファイルシステムに直接アクセス可能
// - useState、useEffect、イベントハンドラは使用不可

async function BlogList() {
  const posts = await db.query('SELECT * FROM posts');
  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>
          <h2>{post.title}</h2>
          <LikeButton postId={post.id} /> {/* Client Component */}
        </li>
      ))}
    </ul>
  );
}
// Client Component
'use client';
// - クライアントでレンダリング(SSR も可能)
// - すべてのフックを使用可能
// - バンドルされてクライアントに送信

function LikeButton({ postId }) {
  const [liked, setLiked] = useState(false);
  return <button onClick={() => setLiked(!liked)}>❤️</button>;
}

Server Actions

'use server';

async function createPost(formData) {
  const title = formData.get('title');
  const content = formData.get('content');
  await db.insert('posts', { title, content });
  revalidatePath('/blog');
}
// Client コンポーネントで Server Action を使用
function NewPostForm() {
  return (
    <form action={createPost}>
      <input name="title" />
      <textarea name="content" />
      <button type="submit">公開</button>
    </form>
  );
}

4. React Compiler:自動パフォーマンス最適化

React Compiler(旧 React Forget)は自動的に memo ロジックを挿入し、手動の useMemouseCallbackmemo が不要になります。

コンパイル前

function ExpensiveComponent({ items, filter }) {
  // 開発者が手動で最適化
  const filtered = useMemo(
    () => items.filter(i => i.category === filter),
    [items, filter]
  );

  const handleClick = useCallback((id) => {
    console.log(id);
  }, []);

  return <List items={filtered} onClick={handleClick} />;
}

コンパイル後(自動最適化)

function ExpensiveComponent({ items, filter }) {
  // 直接書く、コンパイラが自動 memoize
  const filtered = items.filter(i => i.category === filter);
  const handleClick = (id) => console.log(id);
  return <List items={filtered} onClick={handleClick} />;
}

Compiler の有効化

// next.config.js
const nextConfig = {
  experimental: {
    reactCompiler: true,  // または 'unstable' で最新機能
  },
};

Compiler の制限

自動最適化されないシナリオ 理由
受け取った props/mutable オブジェクトの変更 副作用の追跡不可
フック外で作成されたクロージャ ライフサイクルが不明確
arguments オブジェクトの使用 非標準的な使用法

5. ref as prop:forwardRef にさようなら

React 18 の書き方

const Input = forwardRef((props, ref) => {
  return <input ref={ref} {...props} />;
});

React 19 の書き方

function Input({ ref, ...props }) {
  return <input ref={ref} {...props} />;
}

// 使用例
<Input ref={inputRef} placeholder="入力してください" />;

6. Document Metadata

React 19 はコンポーネント内での <title><meta><link> 宣言をネイティブサポートします:

function BlogPost({ post }) {
  return (
    <>
      <title>{post.title}</title>
      <meta name="description" content={post.excerpt} />
      <link rel="canonical" href={`https://example.com/blog/${post.slug}`} />
      <article>
        <h1>{post.title}</h1>
        <p>{post.content}</p>
      </article>
    </>
  );
}

React は自動的にこれらのタグを <head> に引き上げ、異なるルートの metadata を自動的に重複排除・置換します。


7. その他の改善点

useOptimistic:楽観的更新

function LikeButton({ postId, initialLiked }) {
  const [liked, addOptimisticLike] = useOptimistic(initialLiked);

  async function handleLike() {
    addOptimisticLike(true); // 即座にいいねを表示
    await likePost(postId);  // バックグラウンドで同期
  }

  return <button onClick={handleLike}>{liked ? '❤️' : '🤍'}</button>;
}

useSyncExternalStore の改善

getSnapshot が異なるが意味的に等価な値を返すストア(キャッシュ付きデータ取得など)の購読をサポート。

Custom Elements 完全サポート

React 19 は Custom Elements の属性とイベントを適切に処理します:

<my-custom-element someProp="value" onCustomEvent={handler} />

移行チェックリスト

変更 影響 アクション
forwardRefref prop forwardRef を使用する全コンポーネント 移行
useMemo/useCallback 手動 memo Compiler 有効化後に段階的に削除
onSubmitaction フォームコンポーネント Actions にリファクタリング
useContextuse() Context 消費 任意で移行
ref.current.cleanup() ref コールバッククリーンアップ 新しいパターン
文字列 ref 非推奨 レガシーコード コールバック/オブジェクト ref に移行

Codemod ツール

npx react-codemod@latest react-19

forwardRef 移行、文字列 ref 置換など、ほとんどの破壊的変更を自動処理します。

#React#React 19#Server Components#前端框架