メインコンテンツへスキップ

React 19 は、ばらばらの API というより、アプリケーションコードが手動で埋めていたいくつかの穴を React が埋めた感じがします。Forms で非同期 Actions を走らせ、optimistic updates 専用 Hook があり、components が use で promises を読め、refs、context、document metadata 周りの定番 pattern が簡潔になります。

React 19.2 はその上に ActivityuseEffectEvent、より良い性能ツールを載せています。以下は特に有用だと感じる機能と、どこで使うか判断するときに覚えておきたい注意点です。



1. Actions

ActionTransition 内で実行される function です。非同期処理を行いながら、React が周辺の pending state、errors、最終 state update を管理します。

Actions の最も目立つ用途は、<form> の新しい action prop です。onSubmit を横取りし、form を読み、loading flag を追い、手動で reset する代わりに、function を直接渡せます。


function UpdateName() {
  async function updateName(formData: FormData) {
    const name = String(formData.get("name") ?? "")

    await saveName(name)
  }

  return (
    <form action={updateName}>
      <label>
        Name
        <input name="name" />
      </label>
      <SubmitButton />
    </form>
  )
}

Action が成功すると、React は form 内の uncontrolled fields を自動 reset します。同じ function を、submit button ごとに異なる挙動が必要なら button の formAction prop にも指定できます。

Actions は forms に限りませんが、form 連携が最も多い routine state management を削る場所です。



2. useActionStateuseFormStatus

useActionState は Action を、前回の結果から生じる state に接続します。current state、ラップされた Action、isPending を返します。

Action は通常の引数の前に previous state を受け取ります。form Action では第二引数が送信された FormData です。


import { useActionState } from "react"
import { useFormStatus } from "react-dom"

type FormState = {
  message: string
  ok: boolean
}

const initialState: FormState = {
  message: "",
  ok: false,
}

async function saveEmail(
  previousState: FormState,
  formData: FormData
): Promise<FormState> {
  const email = String(formData.get("email") ?? "")

  if (!email.includes("@")) {
    return { message: "Enter a valid email address.", ok: false }
  }

  await subscribe(email)
  return { message: "You are subscribed.", ok: true }
}

function SubmitButton() {
  const { pending } = useFormStatus()

  return (
    <button type="submit" disabled={pending}>
      {pending ? "Subscribing..." : "Subscribe"}
    </button>
  )
}

function NewsletterForm() {
  const [state, formAction] = useActionState(saveEmail, initialState)

  return (
    <form action={formAction}>
      <input name="email" type="email" />
      <SubmitButton />
      <p aria-live="polite">{state.message}</p>
    </form>
  )
}

useFormStatus は parent form の status を読むため、その form の内側 に render された component から呼ぶ必要があります。form を作る同じ component 内で呼んでも、その form の submission は観測できません。

server 結果を UI state にしたいときは useActionState、周囲 form が pending かだけ知ればよい controls には useFormStatus を使います。



3. useOptimistic

network round trip を待ってから UI を更新すると、速いアプリも遅く感じられます。useOptimistic は Action 実行中に 期待結果を即表示 し、完了後に real state と reconcile します。


import { useOptimistic, useState } from "react"

type Message = {
  id: string
  text: string
  sending?: boolean
}

function MessageThread({ initialMessages }: { initialMessages: Message[] }) {
  const [messages, setMessages] = useState(initialMessages)
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages,
    (current, text: string) => [
      ...current,
      { id: crypto.randomUUID(), text, sending: true },
    ]
  )

  async function sendMessage(formData: FormData) {
    const text = String(formData.get("message") ?? "")
    addOptimisticMessage(text)

    const savedMessage = await saveMessage(text)
    setMessages((current) => [...current, savedMessage])
  }

  return (
    <>
      <ul>
        {optimisticMessages.map((message) => (
          <li key={message.id}>
            {message.text}
            {message.sending ? " (sending...)" : ""}
          </li>
        ))}
      </ul>

      <form action={sendMessage}>
        <input name="message" />
        <button type="submit">Send</button>
      </form>
    </>
  )
}

Optimistic value は一時的です。Action 終了後、React は component の real state か props が供給する値に戻ります。source of truth は optimistic reducer の外でも更新が必要です。

Optimistic updates は期待結果が明らかで、失敗を説明・取り消せるときに最適です。destructive operations や、成功を装うとユーザーを誤解させる操作には慎重です。



4. use による Resources の読み取り

新しい use API は render 中に resource を読み取り ます。現状、その resource は通常 PromiseContext です。

use が pending Promise を受け取ると component は suspend し、React は最も近い <Suspense> fallback を表示します。Promise が reject すると、最も近い Error Boundary が error を処理します。


import { Suspense, use } from "react"

type Profile = {
  name: string
  role: string
}

function ProfileCard({ profilePromise }: { profilePromise: Promise<Profile> }) {
  const profile = use(profilePromise)

  return (
    <article>
      <h2>{profile.name}</h2>
      <p>{profile.role}</p>
    </article>
  )
}

function ProfilePage({ profilePromise }: { profilePromise: Promise<Profile> }) {
  return (
    <Suspense fallback={<p>Loading profile...</p>}>
      <ProfileCard profilePromise={profilePromise} />
    </Suspense>
  )
}

Hooks と異なり、use は conditions や loops 内で呼べます。ただし React が component か custom Hook を render している間に呼ぶ必要があります。

重要な Promise の注意点は identity です。Client Component の render ごとに新しい Promise を作ると、繰り返し suspension します。Promise は framework、cache、Server Component から来て、同じ resource を再利用できるべきです。

use は Context を条件付きで読めます:


function Heading({ muted }: { muted: boolean }) {
  if (muted) {
    return <h2 className="muted">Archived</h2>
  }

  const theme = use(ThemeContext)
  return <h2 className={theme}>Active</h2>
}

use は、すべての data-fetching library の代替ではなく、render とまだ ready でない resource の橋渡しだと考えています。



5. Refs と Context の簡素化

React 19 では function components が ref を通常の prop として受け取れます。element を公開するだけの forwardRef は新 components では不要です。


import type { ComponentPropsWithRef } from "react"

function SearchInput(props: ComponentPropsWithRef<"input">) {
  return <input type="search" {...props} />
}

function Search() {
  const inputRef = useRef<HTMLInputElement>(null)

  return <SearchInput ref={inputRef} placeholder="Search" />
}

既存の forwardRef components も動くため、即 rewrite は不要です。React は migration に時間を取った後、将来 forwardRef を deprecate する予定です。

Ref callbacks は cleanup functions を返せるようになりました:


function Canvas() {
  return (
    <canvas
      ref={(node) => {
        if (!node) return

        const observer = new ResizeObserver(() => draw(node))
        observer.observe(node)

        return () => observer.disconnect()
      }}
    />
  )
}

Context providers も短くなりました。.Provider の代わりに Context object を直接 render できます。


// Before
<ThemeContext.Provider value="dark">
  <App />
</ThemeContext.Provider>

// React 19
<ThemeContext value="dark">
  <App />
</ThemeContext>

変更は小さいですが、ほぼすべての React codebase にあった wrappers を取り除きます。



6. Document Metadata と Resources

React 19 は components 内で render された <title><meta><link> tags を理解します。document の <head> に移すため、metadata を所有する route や content の近く に置けます。


function ProductPage({ product }: { product: Product }) {
  return (
    <>
      <title>{product.name} – Acme Store</title>
      <meta name="description" content={product.summary} />
      <link
        rel="canonical"
        href={`https://example.com/products/${product.slug}`}
      />

      <h1>{product.name}</h1>
      <p>{product.summary}</p>
    </>
  )
}

React 19 は stylesheets と scripts も調整します。Stylesheets は precedence を宣言でき、React は関連 CSS を待ってから Suspense content を表示します。複数 components が同じ script を render しても async scripts は deduplicate されます。

React DOM は preconnectpreloadpreinitpreloadModulepreinitModule などの resource hints を公開し、React が element を render する前に resource を知っている場合に使えます。


import { preconnect, preload } from "react-dom"

function ProductImage({ src, alt }: { src: string; alt: string }) {
  preconnect("https://images.example.com")
  preload(src, { as: "image" })

  return <img src={src} alt={alt} />
}

Frameworks が多くを既に処理しているため、直接呼ぶ前に framework の慣習を確認します。



7. Custom Elements と Hydration Errors の改善

React 19 は custom elements を完全サポートします。Server rendering では string や number などの primitive props が attributes になります。ブラウザでは custom element が対応 properties を公開していれば、React は values を properties として assign します。


function Checkout() {
  return (
    <payment-card
      customer-id="customer_123"
      options={{ appearance: "compact" }}
    />
  )
}

非 string 値ごとに React 専用 wrapper を書かずに web components を使いやすくなります。

Hydration diagnostics も有用です。重複した warnings の代わりに、server-rendered HTML と client output の差分を示す統合 error が報告されます。

改善されたメッセージが hydration mismatches を無害にするわけではありません。異なる日付、ブラウザ専用 branch、変化する外部 data、無効な HTML nesting など、真因を早く見つけられます。



8. React 19.2 の Activity

Activity は UI の一部を mounted のまま保ち、可視性と updates の緊急度を制御します。


import { Activity } from "react"

function Workspace({ activeTab }: { activeTab: "editor" | "preview" }) {
  return (
    <>
      <Activity mode={activeTab === "editor" ? "visible" : "hidden"}>
        <Editor />
      </Activity>

      <Activity mode={activeTab === "preview" ? "visible" : "hidden"}>
        <Preview />
      </Activity>
    </>
  )
}

React 19.2 は二つの mode をサポートします:

  • visible — children を表示し、effects を mount し、updates を通常処理する。
  • hidden — children を隠し、effects を unmount し、visible な処理がなくなるまで updates を defer する。

Conditional rendering との有用な違いは preserved state です。隠れた editor は draft、selection、component state を保持し、React は背景で次画面を準備できます。

CSS で visible のままにするのとは異なります。Hidden Activities は effects を cleanup するため、subscriptions など外部 synchronization は正しく停止・再開できる必要があります。



9. React 19.2 の useEffectEvent

Effects はしばしば二つの考え方を混ぜます。dependency に反応すべき synchronization と、最新 props を読むべきだが synchronization を再起動すべきでない event-like logic です。

useEffectEvent は event-like 部分を分離します。


import { useEffect, useEffectEvent } from "react"

function ChatRoom({
  roomId,
  theme,
}: {
  roomId: string
  theme: "light" | "dark"
}) {
  const onConnected = useEffectEvent(() => {
    showNotification("Connected", theme)
  })

  useEffect(() => {
    const connection = createConnection(roomId)

    connection.on("connected", () => onConnected())
    connection.connect()

    return () => connection.disconnect()
  }, [roomId])

  return <p>Room: {roomId}</p>
}

theme を変えても chat room は再接続されませんが、connection event 発火時に onConnected は最新 theme を読みます。

Effect Events は missing dependencies を隠す手段ではありません。 Effect から概念的にトリガーされる logic を表し、同じ component か custom Hook 内の Effects からだけ呼べます。dependency arrays に含めません。

この API には新しい eslint-plugin-react-hooks も必要です。Linter が制限を理解し強制します。



10. React 19.2 の Performance と Server 改善

React 19.2 は Chrome DevTools performance profiles に React Performance Tracks を追加しました。Scheduler tracks は update priorities と、work が scheduled、blocked、rendered、painted されたタイミングを示します。Components tracks は components が render されたタイミングと effects が mount されたタイミングを示します。

React DevTools Profiler より低レベルな視点です。遅い interaction が一つの高コスト component ではなく、scheduling、ブラウザ処理、React rendering の組み合わせのときに使います。

多くのアプリ開発者が framework 経由で触れる二つの追加:

  • cacheSignal は React Server Component 処理に cache() entry の lifetime に紐づく AbortSignal を与え、未使用 requests などをキャンセルできる。
  • Partial pre-rendering APIs は framework が static shell を pre-render し、postponed work を保存して、残り server render を後で resume できる。

React 19.2 は近接 Suspense boundary reveals の batching で streaming SSR を改善し、Node.js に Web Streams サポートを追加しました。React チームは Node 環境では依然 Node Streams APIs を推奨します。高速で compression と自然に相性が良いためです。



11. React 19 へのアップグレード

React、React DOM、TypeScript definitions はまとめて、framework がサポートする最新 patched React 19 release に上げます。


npm install react@^19 react-dom@^19
npm install --save-dev @types/react@^19 @types/react-dom@^19

公式 upgrade guide には common migrations 用 codemods があります。先に確認すべき breaking changes は legacy APIs の削除です:

  • ReactDOM.renderReactDOM.hydratecreateRoothydrateRoot に置き換え。
  • unmountComponentAtNoderoot.unmount() に置き換え。
  • findDOMNode、string refs、this.refs は削除。
  • Legacy Context APIs と function component propTypes は削除。
  • Ref callback の TypeScript ルールは、cleanup functions を返せるためより厳格。

アップグレードと機能採用は別ステップ にします。まず既存アプリを React 19 で動かし、Actions、useActivity が real pattern を簡素化する場所だけ導入します。大規模 rewrite と混ぜない方が major upgrade は debug しやすいです。



まとめ

React 19 で感じるテーマは coordination です。Actions は async mutations と pending、optimistic UI を調整します。use は rendering と resources を調整します。Document APIs は metadata、styles、scripts を調整します。Activity は visible と background work を、useEffectEvent は synchronization と event-like behavior を分けます。

最も使うのは form Actions、useActionStateuseOptimistic、ref as a prop、useEffectEvent だと思います。useActivity、server rendering APIs は framework サポートと architecture 依存度が高いですが、loading、mutations、rendering が一体で設計される React model を示しています。

詳細は公式 React 19 release notesReact 19.2 release notesReact 19 upgrade guide に戻ります。