12k
All articles

React 19が非同期レンダリングについてもたらす変更点

React 19のActions、useActionState、useOptimisticは、React 18の並行レンダリング基盤上の手動非同期状態管理を置き換える。

OpenReplay Team
OpenReplay Team
React 19が非同期レンダリングについてもたらす変更点

React 18は並行レンダリングを導入しました。React 19はその基盤を置き換えるのではなく、これまで手作業で実装していた非同期ワークフローの標準化されたパターンを提供することで、その上に構築されています。

isPending状態の管理、非同期操作全体でのエラーハンドリングの調整、または楽観的更新の手動実装を行ってきた場合、React 19はこれらすべてに対してファーストクラスのAPIを提供します。この記事では、実際に何が変わったのか、そしてメンタルモデルをどのように変えるべきかを説明します。

参考までに、ここで説明するすべてのAPIは、公式Reactドキュメントに記載されています:https://react.dev

重要なポイント

  • React 19は、一般的な非同期パターンのための高レベルの抽象化を追加することで、React 18の並行レンダリングの上に構築されています
  • startTransitionは非同期関数を受け入れるようになり、これらはReactドキュメントで正式にActionsと呼ばれています
  • useActionStateは、保留状態とエラー管理が組み込まれたフォーム処理の定型コードを排除します
  • useOptimisticは、正規の状態によって駆動されるロールバック機能を持つ楽観的更新を標準化します
  • use() APIを使用すると、レンダリング中にプロミスを読み取ることができますが、キャッシュされたプロミスまたはSuspense互換のプロミスが必要です

核心的な変化:手動配管から組み込みパターンへ

React 18は並行レンダリングエンジンを提供しました。React 19はそれを実用的にする抽象化を提供します。

以前は、非同期フォーム送信の処理には、複数の状態変数のやりくりが必要でした:

const [isPending, setIsPending] = useState(false)
const [error, setError] = useState(null)

const handleSubmit = async () => {
  setIsPending(true)
  try {
    await submitData()
  } catch (e) {
    setError(e)
  } finally {
    setIsPending(false)
  }
}

React 19のActionsは、この定型コードを完全に排除します。

React 19のActionsと非同期トランジション

最大の変更点は、startTransitionが非同期関数を受け入れるようになったことです。React 19では、これらの非同期トランジション関数は、Reactドキュメントで正式にActionsと呼ばれています。

const [isPending, startTransition] = useTransition()

const handleSubmit = () => {
  startTransition(async () => {
    const error = await updateName(name)
    if (error) {
      setError(error)
      return
    }
    // 成功時にナビゲーションまたは状態更新を実行
  })
}

React 19は保留中のトランジション状態を追跡し、useTransitionuseActionStateなどのAPIを介して明示的に読み取ることができます。startTransition内のすべての状態更新は一緒にバッチ処理され、非同期作業が完了すると単一の再レンダリングが生成されます。これにより、isPendingがfalseであるがエラー状態がまだ適用されていない中間状態を回避できます。

useActionState:専用のフォーム処理

特にフォームの場合、useActionStateは組み込みの保留状態と結果管理を提供します:

const [error, submitAction, isPending] = useActionState(
  async (previousState, formData) => {
    const error = await updateName(formData.get("name"))
    if (error) return error
    // 成功時にナビゲーションまたはアプリケーション状態を更新
    return null
  },
  null
)

return (
  <form action={submitAction}>
    <input type="text" name="name" />
    <button disabled={isPending}>更新</button>
    {error && <p>{error}</p>}
  </form>
)

Reactは、ネイティブフォームのaction属性を、統合された保留状態と結果状態の処理で強化します。

useOptimistic:即座のUIフィードバック

React 19はuseOptimisticを通じて楽観的更新を標準化します:

const [optimisticName, setOptimisticName] = useOptimistic(currentName)

const submitAction = async (formData) => {
  const newName = formData.get("name")
  setOptimisticName(newName) // UIは即座に更新されます
  const result = await updateName(newName)
  onUpdateName(result)
}

Reactは、アクションが解決した後に親コンポーネントが正規の状態で再レンダリングされると、optimisticNamecurrentNameに戻します。失敗したアクションの場合は、楽観的な値が正しく元に戻るように、親の状態が変更されないようにしてください。

React Suspenseの変更とuse() API

use() APIを使用すると、レンダリング中にプロミスを直接読み取ることができます。フックとは異なり、条件文やループ内で動作します:

function Comments({ commentsPromise }) {
  const comments = use(commentsPromise)
  return comments.map(comment => <p key={comment.id}>{comment}</p>)
}

プロミスが保留中の場合、use()はコンポーネントをサスペンドします。Suspense境界でラップして、フォールバックUIを表示します。

重要な制約:use()は、レンダリング中に作成されたプロミスをサポートしていません。プロミスはキャッシュされているか、Suspense互換のソースから来る必要があります。また、try-catchブロック内でuse()を使用することはできません。エラー境界が代わりに拒否を処理します。

サーバー対応環境では、Reactドキュメントは、データフェッチングのセマンティクスにはasync/awaitを優先し、use()は主に既に管理されているプロミスを消費するために使用することを推奨しています。

変わらなかったこと

React並行UIパターン—基礎となるスケジューリング、中断可能なレンダリング、優先度システム—は、React 18の基盤のままです。React 19は新しい並行性の概念を導入していません。既存の並行機能を活用する高レベルのAPIを提供します。

メンタルモデルの変化は、新しいレンダリングメカニズムを理解することではありません。Reactが以前は自分で管理していた非同期UI状態を処理するようになったことを認識することです。

実用的な影響

**保留状態の手動実装を止める。**手動のisPending変数の代わりにuseTransitionまたはuseActionStateを使用してください。

ミューテーションにActionsを採用する。startTransitionでラップされた任意の非同期関数は、トランジション対応のスケジューリングを持つActionになります。

**レスポンシブなUIにはuseOptimisticを使用する。**このパターンは標準化されており、アドホックに実装するものではありません。

**データフェッチングにはuse()とSuspenseを組み合わせる。**ただし、キャッシング要件を覚えておいてください—これは、プロミスの安定性を管理するフレームワークやライブラリで最もうまく機能します。

結論

React 19の非同期レンダリングは新しいパラダイムではありません。並行Reactを日常的な非同期ワークフローに実用的にする、欠けていた抽象化レイヤーです。保留状態、フォーム処理、楽観的更新、プロミス解決のための組み込みAPIを提供することで、React 19は、React 18が並行レンダリングを導入して以来、開発者が書いてきた定型コードを排除します。基盤は同じままです—変わったのは、これらのパターンがどれほどアクセスしやすく標準化されたかです。

よくある質問

Next.jsのようなフレームワークなしでReact 19のActionsを使用できますか?

はい。ActionsはあらゆるReact 19アプリケーションで動作します。Next.jsのようなフレームワークは追加のサーバーサイド統合を提供しますが、非同期関数を使用するuseTransition、useActionState、useOptimisticなどのコアAPIは、フレームワークの依存関係なしでクライアントサイドのReactアプリで機能します。

楽観的更新が失敗した場合はどうなりますか?ロールバックを手動で処理する必要がありますか?

完全にではありません。useOptimisticは、親コンポーネントが変更されていない正規の状態で再レンダリングされると、元の値に戻ります。ロールバックが正しく発生するように、失敗したアクションがその状態を更新しないようにする必要があります。

既存のuseStateベースのローディング状態をすべてuseTransitionに置き換えるべきですか?

必ずしもそうではありません。useTransitionは、Reactに現在のUIをレスポンシブに保ちたい緊急でない更新に最適です。インタラクションをブロックすべき重要なローディングインジケーターの場合、従来のuseStateパターンが依然として適切な場合があります。

コンポーネント内でfetch呼び出しと直接use() APIを使用できますか?

技術的には可能ですが、慎重なプロミス管理が必要です。use()に渡されるプロミスは、レンダリング全体で安定している必要があります。つまり、レンダリング中に作成することはできません。プロミスの安定性を確保するために、キャッシングレイヤー、データフェッチングライブラリ、またはフレームワークレベルのデータロードを使用してください。

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.