1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

React 19の新機能まとめ — Actions・新Hooks・サーバーコンポーネントを実例で解説

1
Posted at

この記事は AI(Claude by Anthropic)を利用して作成しました。
公式ドキュメントや複数の技術記事をもとに生成・編集していますが、誤りが含まれる可能性があります。コードは実際の環境で動作確認の上ご利用ください。正確な情報は末尾の参考リンク(公式ドキュメント)を優先してください。

はじめに

2024年12月5日、フロントエンド開発の定番ライブラリ React の最新安定版 React 19 がリリースされました。このバージョンは単なるマイナーアップデートではなく、非同期処理・フォーム管理・サーバーサイドレンダリングの考え方そのものをリフレッシュする内容となっています。

本記事では中級フロントエンドエンジニアを対象に、React 19 の主要な新機能を実例コード付きで解説します。


1. Actions — 非同期処理が劇的にシンプルに

従来の問題点

React 18 までは、フォーム送信などの非同期処理において、pending 状態・エラー・楽観的更新をすべて手動で管理する必要がありました。

// React 18 以前の典型的なフォーム送信
const [isPending, setIsPending] = useState(false);
const [error, setError] = useState<string | null>(null);

const handleSubmit = async (name: string) => {
  setIsPending(true);
  setError(null);
  try {
    await updateUserName(name);
  } catch (e) {
    setError("更新に失敗しました");
  } finally {
    setIsPending(false);
  }
};

React 19 の Actions

React 19 では、非同期関数を「Actions」として useTransition に渡すことで、pending 状態・エラー・フォームの処理を自動で管理できるようになりました。

// React 19 の Actions を使った書き方
import { useTransition } from "react";

function UpdateNameForm() {
  const [isPending, startTransition] = useTransition();

  const handleSubmit = (formData: FormData) => {
    startTransition(async () => {
      await updateUserName(formData.get("name") as string);
    });
  };

  return (
    <form action={handleSubmit}>
      <input name="name" />
      <button type="submit" disabled={isPending}>
        {isPending ? "更新中..." : "更新"}
      </button>
    </form>
  );
}

ボイラープレートが大幅に減り、コードの意図が明確になります。


2. 新しい Hooks

useActionState

Actions の変更に伴い追加された新フック。ラップされたアクションが呼び出されると、アクションの最後の結果と pending 状態を返します。

import { useActionState } from "react";

async function submitAction(prevState: string, formData: FormData) {
  const name = formData.get("name") as string;
  await updateUserName(name);
  return `${name} に更新しました`;
}

function NameForm() {
  const [message, dispatch, isPending] = useActionState(submitAction, "");

  return (
    <form action={dispatch}>
      <input name="name" />
      <button disabled={isPending}>送信</button>
      {message && <p>{message}</p>}
    </form>
  );
}

useOptimistic — 楽観的 UI 更新

非同期リクエストの実行中に最終状態を楽観的に表示できるフック。サーバーの応答を待たずに UI を先に更新し、エラー時には自動でロールバックします。

import { useOptimistic, useTransition } from "react";

function TodoList({ todos }: { todos: string[] }) {
  const [optimisticTodos, addOptimisticTodo] = useOptimistic(
    todos,
    (state, newTodo: string) => [...state, newTodo]
  );
  const [, startTransition] = useTransition();

  const addTodo = (text: string) => {
    startTransition(async () => {
      addOptimisticTodo(text); // 即座に UI に反映
      await saveTodoToServer(text); // 実際の保存
    });
  };

  return (
    <ul>
      {optimisticTodos.map((todo, i) => <li key={i}>{todo}</li>)}
    </ul>
  );
}

use — Promise をレンダー内で読む

use は render でリソースを読み取るための新しい API。Promise が解決されるまで React は Suspend します。

import { use, Suspense } from "react";

function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
  const user = use(userPromise); // Promise を直接 unwrap
  return <p>{user.name}</p>;
}

function App() {
  const userPromise = fetchUser(1);
  return (
    <Suspense fallback={<p>読み込み中...</p>}>
      <UserProfile userPromise={userPromise} />
    </Suspense>
  );
}

3. サーバーコンポーネントの正式安定化

React 19 では、サーバーコンポーネントが正式に安定版として導入されました。サーバー上でレンダリングされるコンポーネントにより、クライアントへ送信する JavaScript 量が減り、初期表示が高速になります。

// app/page.tsx(Next.js App Router)
// "use client" がなければ自動的にサーバーコンポーネント
export default async function Page() {
  // サーバー側で直接 DB アクセス可能
  const posts = await db.posts.findMany();

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

クライアントコンポーネントはインタラクティブな部分だけに限定することで、バンドルサイズを最小化できます。


4. ref が props として使えるように

React 18 でも forwardRef を使えば ref を子コンポーネントへ渡すこと自体は可能でしたが、React 19 では forwardRef のラップが不要になり、通常の prop と同じように渡せるようになりました。

// React 18 — forwardRef でのラップが必須だった
const Input = React.forwardRef<HTMLInputElement, React.InputHTMLAttributes<HTMLInputElement>>(
  ({ ...props }, ref) => <input ref={ref} {...props} />
);

// React 19 — forwardRef 不要、ref を普通の prop として受け取れる
function Input({ ref, ...props }: React.InputHTMLAttributes<HTMLInputElement> & { ref?: React.Ref<HTMLInputElement> }) {
  return <input ref={ref} {...props} />;
}

// 呼び出し側(どちらも同じ書き方)
const inputRef = useRef<HTMLInputElement>(null);
<Input ref={inputRef} placeholder="名前を入力" />;

forwardRef は React 19 では非推奨となり、将来的に削除される予定です。


まとめ

機能 解決する課題
Actions / useActionState 非同期処理の状態管理ボイラープレートを削減
useOptimistic 楽観的 UI 更新を宣言的に記述
use Promise を Suspense と組み合わせてシンプルに扱う
サーバーコンポーネント 初期表示の高速化・JS バンドルサイズの削減
ref as props forwardRef 廃止でコードがシンプルに

React 19 は「書くコードを減らす」方向への大きな進化です。特に Actions 周りは既存プロジェクトのフォーム処理を大幅に簡潔にできます。まずは useActionStateuseOptimistic から試してみることをおすすめします。


この記事について

本記事は Claude(Anthropic) を使用して構成・文章・コード例を生成し、筆者が内容を確認・編集して公開しています。

AI を利用した記事を読む際には以下の点にご注意ください。

  • コードは必ず手元で動作確認してください。 AI はそれらしいコードを生成しますが、バージョン差異や細かい仕様の誤りが混入することがあります。
  • 最新情報は公式ドキュメントを参照してください。 React は活発に開発が続いており、記事公開後に仕様が変わる場合があります。
  • AIの知識にはカットオフがあります。 React 19.1 以降のマイナーアップデートについては本記事では触れていません。

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?