form 元素是 React 的未來

大家好,我卡頌。

請思考一個問題:如果有一個HTML標籤,React圍繞他專門出了 2 個hook,那這個標籤對React未來的發展一定非常重要,這沒毛病吧?

這個標籤就是 —— form

React圍繞form新出瞭如下 2 個hook

本文會聊聊React圍繞form的佈局與發展。

Next.js 的發展歷程

說到React未來的發展,必須從Next.js聊起。畢竟,React團隊成員不是加入Next團隊,就是在加入的路上。

web開發中涉及到前後端交互的部分主要包括:

  1. 根據後端數據渲染前端頁面

  2. 根據前端用戶輸入保存數據到後端

Next.js的發展主要圍繞以上兩點展開。

根據後端數據渲染前端頁面

前期,Next.js的主打特性是SSRSSG。也就是把 「根據後端數據渲染前端頁面」 的過程從前端挪到後端。

這個時期的Next.js路由被稱爲Pages Router

時間來到Next.js v13,以RSC(React Server Component)爲核心的App Router取代Pages Router成爲默認配置。

很多朋友不熟悉RSC,認爲他是實驗特性。實際上,RSC藉由Next.js已經落地了。

一句話理解RSC —— 客戶端組件(在瀏覽器渲染的React組件)可以根據依賴分爲兩部分:

「根據後端數據渲染前端頁面」 角度看:

根據前端用戶輸入保存數據到後端

聊完了 「根據後端數據渲染前端頁面」,那麼,圍繞 「根據前端用戶輸入保存數據到後端」Next.js能做哪些優化?

這就要提到Next.js的實驗特性 —— Server Action

Server Action

「根據前端用戶輸入保存數據到後端」 的常見場景是 「表單提交」,通常我們會在formonSubmit事件中做後續處理:

function Form() {
  function submit() {
    // ...處理formData的邏輯
    // ...發送請求的邏輯
  }
  return (
    <form onSubmit={submit}>
      <input type="text"/>
      <input type="text"/>
    </form>
  )
}

以上代碼有什麼可改進的地方呢?

從用戶體驗的角度看,如果前端禁用了JS,那麼React不能運行,上述交互失效。如果在禁用JS的情況下也能提交表單就好了。

從開發體驗的角度看,submit方法會發起請求,後端再根據請求攜帶的formData操作數據庫,比較繁瑣。如果在submit方法內能直接操作數據庫就好了。

Server Action特性就是爲了實現以上 2 個目標。

首先來看第一個目標。

目標 1

HTML原生的form元素有個action屬性,可以接收一個url。當提交表單(比如點擊typesubmit的按鈕)後formData會提交給該url

<form action="/action_page.php" method="get">
  <label for="fname">First name:</label>
  <input type="text" id="fname" ><br><br>
  <label for="lname">Last name:</label>
  <input type="text" id="lname" ><br><br>
  <input type="submit" value="Submit">
</form>

由於 「提交表單」 的行爲是HTML原生支持的,所以在禁用JS的情況下也能執行。

這就是禁用JS也能提交表單的理論基礎。

目標 2

React擴展了formaction屬性,讓他除了支持url,還能支持回調函數,比如:

function App() {
  function submit(data) {
    // ...
  }
  return (
    <form action={submit}>
      <! -- 省略 -->
    </form>
  );

如果這個回調函數內是前端執行的邏輯,則被稱爲client action,比如下面這樣:

  async function submit(data) {
    await const res = saveData(data);
    // ...
  }

如果這個函數內是後端執行的邏輯,則被稱爲server action,比如下面這樣:

  "use server"
  
  async function submit(data) {
    const userID = cookies().get("userID")?.value;
    await db.users.update(userID, data);
    // ...
  }

"use server"標記代表這是個server action

如果是server action,那麼發起的請求類型是multipart/form-data(即表單提交):

響應類型則是RSC協議

也就是說,有了server action,開發者可以直接在formaction屬性(或者buttonformAction屬性等其他幾種屬性)內書寫後端邏輯,並且在瀏覽器禁用JS的情況下這些邏輯也能執行。

2 個新 hook

爲了更好的服務server actionReact團隊新出了 2 個hook用於提高form場景下的用戶體驗:

當前,這 2 個hook的介紹只能在 Next.js 文檔 [1](而不是React文檔)中看到。

useOptimistic主要用來優化 「提交數據」 的用戶體驗。

比如,在 「點贊」 的場景,通常邏輯是:

  1. 點擊點贊按鈕

  2. 發起點贊請求

  3. 點贊成功,前端顯示點贊效果

但爲了用戶體驗的流暢,前端通常會把邏輯做成:

  1. 點擊點贊按鈕

  2. 前端顯示點贊效果(同時發起點贊請求)

  3. 根據請求結果,如果點贊成功則不做處理,如果點贊失敗則重置按鈕

useOptimistic的本質就是在狀態層面實現上述效果。

useFormStatus則用於在表單提交過程中顯示pending狀態:

function ButtonDisabledWhilePending({action, children}) {
  const {pending} = useFormStatus();
  return (
    <button disabled={pending} formAction={action}>
      {children}
    </button>
  );
}

有同學可能會疑惑:useFormStatus沒有傳參,他怎麼知道對應哪個form

實際上,爲了實現useFormStatusReact在源碼內爲所有HostComponent(即原生HTML元素對應組件,比如<div/>)定製了一個context

當某個form觸發表單提交時,context的值會被更新爲這個form的數據。useFormStatus本身僅僅是useContext(上述context)

總結

可以發現,不管是useFormStatususeOptimistic還是最近 1~2 年新出的hook(比如useIduseMutableSource),我們開發者都很少會用到。

因爲這些hook都是爲上層框架(主要是Next.js)提供的。

React早已完成他作爲前端框架的使命。在他生命的後半程,他將作爲上層框架的 「操作系統」 而存在。

server actionNext.js的未來,Next.jsReact的未來。所以,React的未來會圍繞form元素持續佈局。

參考資料

[1] Next.js 文檔: https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions#experimental-useoptimistic

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/9CZ0IP2fnAirFF0YMMFV2g