form 元素是 React 的未來
大家好,我卡頌。
請思考一個問題:如果有一個HTML
標籤,React
圍繞他專門出了 2 個hook
,那這個標籤對React
未來的發展一定非常重要,這沒毛病吧?
這個標籤就是 —— form
。
React
圍繞form
新出瞭如下 2 個hook
:
-
useOptimistic
-
useFormStatus
本文會聊聊React
圍繞form
的佈局與發展。
Next.js 的發展歷程
說到React
未來的發展,必須從Next.js
聊起。畢竟,React
團隊成員不是加入Next
團隊,就是在加入的路上。
web
開發中涉及到前後端交互的部分主要包括:
-
根據後端數據渲染前端頁面
-
根據前端用戶輸入保存數據到後端
Next.js
的發展主要圍繞以上兩點展開。
根據後端數據渲染前端頁面
前期,Next.js
的主打特性是SSR
、SSG
。也就是把 「根據後端數據渲染前端頁面」 的過程從前端挪到後端。
這個時期的Next.js
路由被稱爲Pages Router
。
時間來到Next.js v13
,以RSC
(React Server Component)爲核心的App Router
取代Pages Router
成爲默認配置。
很多朋友不熟悉RSC
,認爲他是實驗特性。實際上,RSC
藉由Next.js
已經落地了。
一句話理解RSC
—— 客戶端組件(在瀏覽器渲染的React
組件)可以根據依賴分爲兩部分:
-
依賴數據源(比如數據庫、文件系統)的組件,可以作爲
RSC
(服務端組件) -
依賴狀態(比如
state
、props
、context
)的組件,可以作爲客戶端組件
從 「根據後端數據渲染前端頁面」 角度看:
-
SSR
、SSG
是頁面級別的(服務端渲染呈現的是整個頁面) -
RSC
是組件級別的(服務端組件請求數據源)
根據前端用戶輸入保存數據到後端
聊完了 「根據後端數據渲染前端頁面」,那麼,圍繞 「根據前端用戶輸入保存數據到後端」,Next.js
能做哪些優化?
這就要提到Next.js
的實驗特性 —— Server Action
。
Server Action
「根據前端用戶輸入保存數據到後端」 的常見場景是 「表單提交」,通常我們會在form
的onSubmit
事件中做後續處理:
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
。當提交表單(比如點擊type
爲submit
的按鈕)後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
擴展了form
的action
屬性,讓他除了支持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
,開發者可以直接在form
的action
屬性(或者button
的formAction
屬性等其他幾種屬性)內書寫後端邏輯,並且在瀏覽器禁用JS
的情況下這些邏輯也能執行。
2 個新 hook
爲了更好的服務server action
,React
團隊新出了 2 個hook
用於提高form
場景下的用戶體驗:
-
useOptimistic
-
useFormStatus
當前,這 2 個hook
的介紹只能在 Next.js 文檔 [1](而不是React
文檔)中看到。
useOptimistic
主要用來優化 「提交數據」 的用戶體驗。
比如,在 「點贊」 的場景,通常邏輯是:
-
點擊點贊按鈕
-
發起點贊請求
-
點贊成功,前端顯示點贊效果
但爲了用戶體驗的流暢,前端通常會把邏輯做成:
-
點擊點贊按鈕
-
前端顯示點贊效果(同時發起點贊請求)
-
根據請求結果,如果點贊成功則不做處理,如果點贊失敗則重置按鈕
useOptimistic
的本質就是在狀態層面實現上述效果。
useFormStatus
則用於在表單提交過程中顯示pending
狀態:
function ButtonDisabledWhilePending({action, children}) {
const {pending} = useFormStatus();
return (
<button disabled={pending} formAction={action}>
{children}
</button>
);
}
有同學可能會疑惑:useFormStatus
沒有傳參,他怎麼知道對應哪個form
?
實際上,爲了實現useFormStatus
,React
在源碼內爲所有HostComponent
(即原生HTML
元素對應組件,比如<div/>
)定製了一個context
。
當某個form
觸發表單提交時,context
的值會被更新爲這個form
的數據。useFormStatus
本身僅僅是useContext(上述context)
。
總結
可以發現,不管是useFormStatus
、useOptimistic
還是最近 1~2 年新出的hook
(比如useId
、useMutableSource
),我們開發者都很少會用到。
因爲這些hook
都是爲上層框架(主要是Next.js
)提供的。
React
早已完成他作爲前端框架的使命。在他生命的後半程,他將作爲上層框架的 「操作系統」 而存在。
server action
是Next.js
的未來,Next.js
是React
的未來。所以,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