React 19 は、ばらばらの API というより、アプリケーションコードが手動で埋めていたいくつかの穴を React が埋めた感じがします。Forms で非同期 Actions を走らせ、optimistic updates 専用 Hook があり、components が use で promises を読め、refs、context、document metadata 周りの定番 pattern が簡潔になります。
React 19.2 はその上に Activity、useEffectEvent、より良い性能ツールを載せています。以下は特に有用だと感じる機能と、どこで使うか判断するときに覚えておきたい注意点です。
1. Actions
Action は Transition 内で実行される 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. useActionState と useFormStatus
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 は通常 Promise か Context です。
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 は preconnect、preload、preinit、preloadModule、preinitModule などの 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.renderとReactDOM.hydrateはcreateRootとhydrateRootに置き換え。unmountComponentAtNodeはroot.unmount()に置き換え。findDOMNode、string refs、this.refsは削除。- Legacy Context APIs と function component
propTypesは削除。 - Ref callback の TypeScript ルールは、cleanup functions を返せるためより厳格。
アップグレードと機能採用は別ステップ にします。まず既存アプリを React 19 で動かし、Actions、use、Activity が 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、useActionState、useOptimistic、ref as a prop、useEffectEvent だと思います。use、Activity、server rendering APIs は framework サポートと architecture 依存度が高いですが、loading、mutations、rendering が一体で設計される React model を示しています。
詳細は公式 React 19 release notes、React 19.2 release notes、React 19 upgrade guide に戻ります。