WorkBlog

React 19 新功能完整教學:use Hook、Actions 與表單狀態管理實戰指南

楊靜宜
React 19 新功能完整教學:use Hook、Actions 與表單狀態管理實戰指南

如果你是 React 開發者,2025 年底到 2026 年初這段時間大概是最興奮的一段日子了。React 19 正式穩定版的發布,帶來了好幾個讓人眼睛一亮的新功能。老實說,我自己在第一次看到 use() 這個 API 的時候,還以為是社群的玩笑話——一個叫做 use 的 Hook?但實際用過之後,才發現它真的解決了不少以前很頭痛的問題。

這篇文章會從頭到尾帶你走過 React 19 最重要的幾個新功能,包括 use()useActionStateuseOptimisticuseFormStatus,以及全新的 Actions 機制。不管你是剛開始學 React 還是已經用了好幾年,相信這篇都能幫你快速掌握這些新工具。

React 19 帶來了什麼改變

在聊具體的 API 之前,我覺得有必要先講一下 React 19 的整體設計方向。過去幾個版本,React 團隊一直在做一件事:降低開發者處理非同步操作的心智負擔。從 Suspense 開始、到 Server Components,再到現在的 Actions,你可以很明顯感覺到他們想讓「資料取得」跟「狀態更新」這兩件事變得更直覺。

React 19 不再需要 forwardRef,ref 可以直接當作 props 傳遞,這點光是少寫那些包裝程式碼就值得升級了。另外,Context 的用法也簡化了,以前要寫 SomeContext.Provider,現在直接用 <SomeContext> 就好。聽起來是小事,但累積起來真的省了不少程式碼。

use() API:讀取 Promise 與 Context 的新方式

好,讓我們直接進入重頭戲。use() 是 React 19 最具話題性的新 API,它跟其他 Hook 有一個根本性的不同——它可以在條件式跟迴圈裡面使用。沒錯,你沒看錯,那個「Hook 不能在 if 裡面呼叫」的規則,use() 是例外。

它的用途主要有兩個:讀取 Promise 的值,以及讀取 Context。先來看讀取 Promise 的部分:

import { use, Suspense } from "react";

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

// 使用時搭配 Suspense
<Suspense fallback={<p>載入中...</p>}>
  <UserProfile userPromise={fetchUser(userId)} />
</Suspense>

看到了嗎?不用 useEffect,不用自己管 loading 跟 error 狀態,直接把 Promise 丟進 use() 就能拿到解析後的值。當 Promise 還沒 resolve 的時候,React 會自動幫你觸發最近的 Suspense boundary。這跟以前要寫一堆 useStateuseEffect 的做法比起來,程式碼量大概少了六七成。如果你之前有遇過 React useEffect 無限迴圈解法 這類問題,use() 的出現某種程度上直接繞開了那些陷阱。

至於讀取 Context 的用法也很直觀:

function ThemeButton() {
  if (shouldUseTheme) {
    const theme = use(ThemeContext);
    return <button style={{ color: theme.primary }}>按鈕</button>;
  }
  return <button>預設按鈕</button>;
}

這在以前用 useContext 是做不到的,因為 Hook 必須放在元件最頂層。

useActionState:表單狀態管理的新標準

如果你寫過 React 表單,一定知道那種痛苦——一堆 useState、一個 handleSubmit、loading 狀態、error 處理、成功訊息⋯⋯光是一個簡單的登入表單就能寫到五六十行。useActionState 就是來解決這個問題的。

import { useActionState } from "react";

async function submitComment(prevState, formData) {
  const comment = formData.get("comment");
  const result = await postComment(comment);
  if (result.error) {
    return { error: result.error, comment };
  }
  return { success: true, comment: "" };
}

function CommentForm() {
  const [state, formAction, isPending] = useActionState(submitComment, {
    error: null,
    comment: ""
  });

  return (
    <form action={formAction}>
      <textarea name="comment" defaultValue={state.comment} />
      {state.error && <p className="error">{state.error}</p>}
      <button disabled={isPending}>
        {isPending ? "送出中..." : "送出留言"}
      </button>
    </form>
  );
}

注意看,這邊的 form 直接使用了 action 屬性,這是 React 19 引入的 Actions 機制的一部分(後面會詳細講)。useActionState 回傳三個東西:目前的狀態、要綁定到 form 的 action 函式、還有一個表示是否正在提交的 boolean。整個流程變得非常清晰。

useOptimistic:打造流暢的樂觀 UI

做過社群類應用的開發者一定碰過這個需求:使用者按了讚之後,不要等 API 回傳才更新畫面,而是先樂觀地假設操作會成功,立刻更新 UI,如果失敗再回滾。以前要自己處理這些邏輯其實滿煩的,React 19 的 useOptimistic 讓這件事變得簡單很多。

import { useOptimistic } from "react";

function MessageList({ messages, sendMessage }) {
  const [optimisticMessages, addOptimistic] = useOptimistic(
    messages,
    (currentMessages, newMessage) => [
      ...currentMessages,
      { text: newMessage, sending: true }
    ]
  );

  async function handleSend(formData) {
    const text = formData.get("message");
    addOptimistic(text);
    await sendMessage(text);
  }

  return (
    <div>
      {optimisticMessages.map((msg, i) => (
        <p key={i} style={{ opacity: msg.sending ? 0.6 : 1 }}>
          {msg.text}
        </p>
      ))}
      <form action={handleSend}>
        <input name="message" />
        <button type="submit">送出</button>
      </form>
    </div>
  );
}

當非同步操作進行中,optimisticMessages 會包含那個樂觀新增的訊息;操作完成後,React 會自動用實際的 messages 取代樂觀狀態。如果操作失敗,樂觀更新也會被自動回滾。這樣的使用者體驗,在即時聊天或社群互動類的應用中特別重要。

useFormStatus:掌握表單提交狀態

useFormStatus 是一個比較小但非常實用的 Hook,它讓你在表單的子元件裡面取得表單的提交狀態,不用透過 props 一層層往下傳。

import { useFormStatus } from "react-dom";

function SubmitButton() {
  const { pending, data, method } = useFormStatus();
  return (
    <button type="submit" disabled={pending}>
      {pending ? "處理中..." : "確認送出"}
    </button>
  );
}

這個 Hook 必須在 <form> 的子元件中使用才有效。它回傳的 pending 屬性告訴你表單是不是正在提交中,data 則包含了正在提交的 FormData。這對於共用的按鈕元件特別好用——你可以寫一個通用的 SubmitButton,放到任何表單裡都能自動顯示載入狀態。

Actions 機制與 form action 整合

前面幾個範例你可能已經注意到了,React 19 允許你在 <form>action 屬性上直接傳入一個函式。這就是 Actions 機制的核心概念。在 React 19 裡,任何使用了 async transition 的函式都被稱為 Action。

Actions 提供了幾個重要的特性:自動管理 pending 狀態、內建的錯誤處理(搭配 Error Boundary)、支援樂觀更新、以及表單的漸進增強。所謂漸進增強,就是即使 JavaScript 還沒載入完,表單依然可以透過原生 HTML 機制提交——這在搭配 Server Components 的時候特別有用。

如果你的專案有用到 Turbopack 打包工具,React 19 的 Actions 搭配 Next.js 的 Server Actions 可以讓你在 Server Component 裡面直接寫資料庫操作,完全不用另外建 API endpoint,開發效率提升非常明顯。

與 Server Components 的完美搭配

React 19 另一個重大更新是 Server Components 正式成為穩定功能。雖然 Server Components 主要是透過框架(像 Next.js)來使用,但 React 19 提供了完整的底層支援。搭配前面提到的 use() API,你可以在 Server Component 裡取得資料、然後用 use() 在 Client Component 裡讀取,整個資料流變得非常順暢。

舉個實際的例子:

// Server Component
async function PostPage({ postId }) {
  const commentsPromise = fetchComments(postId);
  return (
    <article>
      <PostContent id={postId} />
      <Suspense fallback={<CommentSkeleton />}>
        <Comments commentsPromise={commentsPromise} />
      </Suspense>
    </article>
  );
}

// Client Component
"use client";
function Comments({ commentsPromise }) {
  const comments = use(commentsPromise);
  return comments.map(c => <Comment key={c.id} {...c} />);
}

Server Component 負責發起請求,但不等它完成就先渲染頁面;Client Component 透過 use() 讀取資料,搭配 Suspense 在等待時顯示骨架屏。這種模式在大型應用中可以大幅改善首屏載入速度。

另外值得一提的是,React 19 現在原生支援文件 metadata——你可以在任何元件裡面使用 <title><meta><link> 標籤,React 會自動把它們提升到 <head> 裡面。這對 SEO 跟社群分享來說非常方便,以前要用 react-helmet 之類的第三方套件才能做到。如果你對前端佈局也有興趣,可以看看 CSS Grid 切版教學,搭配 React 19 的新功能可以打造出非常現代化的網頁應用。

實務建議與遷移策略

講了這麼多新功能,現在來聊聊實際導入的策略。我的建議是不要急著把整個專案一次升級,而是採用漸進式遷移。React 19 的好處是這些新 API 跟舊的寫法可以共存,你不需要把所有的 useEffect 資料取得都改成 use(),也不用把所有表單都改成 Actions。

第一步,先升級到 React 19 並確保現有程式碼正常運作。React 19 移除了一些已經被標記為 deprecated 的 API(像是 ReactDOM.renderpropTypesdefaultProps 等),所以先處理這些破壞性變更。

第二步,在新的功能開發中優先使用新的 API。新建立的表單就用 useActionState,新的資料取得就用 use() 搭配 Suspense。

第三步,逐步重構舊有的程式碼。把那些有明顯 pain point 的地方(比如複雜的表單、需要樂觀更新的地方)先改過來。

如果你的專案是用 Docker 部署的,遷移時記得更新 Node.js 版本到 18 以上,React 19 需要較新的 JavaScript 引擎特性。可以參考 Docker Compose 部署教學 來更新你的容器設定。

總結

React 19 是一次非常有感的升級。use() 讓非同步資料讀取變得前所未有地簡單,useActionState 大幅簡化了表單處理的樣板程式碼,useOptimistic 讓樂觀 UI 不再是高級技巧,而是開箱即用的標準工具。再加上 Actions 機制跟 Server Components 的成熟,React 的開發體驗確實往前邁了一大步。

我自己的感受是,React 19 讓你可以把更多精力放在產品邏輯上,而不是跟框架本身搏鬥。如果你還在觀望要不要升級,我的建議是:值得。現在就開始吧,從一個小功能模組開始嘗試這些新 API,你會很快感受到差異的。

楊靜宜

前端工程師,CSS 重度愛好者。Tailwind CSS 佈道者。

CSSTailwind前端效能無障礙設計

你可能也喜歡

探索其他領域的精選好文