React 新 Hook:UseFormStatus 使用詳解
一、action 支持異步回調(diào)
一個(gè)令人振奮的特性就是,在 React19 中,action 支持傳入異步回調(diào)函數(shù)。
例如如下代碼:
async function formAction(formdata) {
const title = formdata.get('title')
const content = formdata.get('content')
// 簡(jiǎn)單校驗(yàn)
if (!title || !content) {
return;
}
await new Promise(resolve => setTimeout(resolve, 1000))
setPosts(posts => [...posts, {title,content}])
}
<form action={formAction}>
...
</form>
有了這個(gè)特性的支持,我們就可以非常方便的利用它來(lái)實(shí)現(xiàn)一些上傳邏輯。不過(guò)一個(gè)小小的需求就是,點(diǎn)擊提交之后,接口請(qǐng)求的過(guò)程中,我們希望按鈕處于禁用狀態(tài),那應(yīng)該怎么辦呢?
React 19 提供了名為 useFormStatus 的 hook 來(lái)幫助我們做到這個(gè)事情。
二、useFormStatus
和別的 hook 不同的是,我們需要從 react-dom 中獲取到它的引用。
import { useFormStatus } from "react-dom";
useFormStatus 能夠在 form 元素的子組件中,獲取到表單提交時(shí)的 pending 狀態(tài)和表單內(nèi)容。
?
與 form 同屬于一個(gè)組件,獲取不到,只能是封裝后的子組件才能獲取到。
const { pending, data, method, action } = useFormStatus();
pending 為 true 時(shí),表示請(qǐng)求正在進(jìn)行。我們可以利用這個(gè)值的變化在提交按鈕上設(shè)置 Loading 樣式。
data 格式為 FormData,表示此次提交里表單的所有內(nèi)容。
method 表示我們?cè)谔峤粫r(shí),所采用的請(qǐng)求方式,默認(rèn)值為 get。
?
需要注意的是,提交方式并不需要通過(guò)如下方式設(shè)置,這樣做會(huì)報(bào)錯(cuò)。
<form method="post">
</form>
action 就是在 form 元素中的 action 回調(diào)函數(shù)的引用。
三、案例一:提交時(shí)設(shè)置禁用按鈕
為了防止重復(fù)提交,我們希望在提交時(shí)就馬上禁用按鈕,等到提交完成之后再恢復(fù)按鈕的點(diǎn)擊。與此同時(shí),我們可能還需要在 UI 交互上做出一些提示,讓用戶知道當(dāng)前正在發(fā)送請(qǐng)求。
交互效果如下:
這里主要是針對(duì)提交按鈕做的操作,因此我們需要單獨(dú)將提交按鈕相關(guān)的部分拿出來(lái)封裝成為一個(gè)子組件,并在子組件中利用 useFormStatus
獲取異步 action 的 pending 狀態(tài)。
代碼非常的簡(jiǎn)單,如下所示:
function SubmitButton() {
const {pending} = useFormStatus()
return (
<div className="form_item">
<button
className='primary'
type='submit'
disabled={pending}
>
{pending ? 'Submitting...' : 'Submit'}
</button>
</div>
)
}
然后在 form 元素中使用該組件即可。
<form actinotallow={formAction} method="post">
<div className="form_item">
<div className="label">Title</div>
<input name='title' type="text" placeholder='Enter title' />
</div>
<div className="form_item">
<div className="label">Name</div>
<input name='content' type="text" placeholder='Enter you name' />
</div>
<SubmitButton />
</form>
四、案例二:提交時(shí)禁止輸入內(nèi)容
通常情況下,我們也希望在表單提交時(shí),不允許輸入內(nèi)容。useFormStatus 可以很容易幫我們做到這一點(diǎn)。
實(shí)現(xiàn)非常簡(jiǎn)單,我們將某一個(gè)字段單獨(dú)封裝到子組件中,利用 useFormStatus 提供的 pending 狀態(tài)來(lái)判斷是否禁用輸入,代碼如下:
function Input2({required, name}) {
const {pending} = useFormStatus()
return (
<div className="form_item">
<div className="label">Name</div>
<input
name={name}
type="text"
placeholder='Enter you name'
disabled={pending}
/>
</div>
)
}
<form action={formAction}>
<div className="form_item">
<div className="label">Title</div>
<input name='title' type="text" placeholder='Enter title' />
</div>
<Input2 required name='content' />
<SubmitButton />
</form>
五、總結(jié)
useFormStatus 是結(jié)合 action 異步請(qǐng)求時(shí)使用的 hook,它們是對(duì) HTML 表單能力的增強(qiáng)。因此我們可以借助他們與 HTML 表單元素自身支持的特性實(shí)現(xiàn)更復(fù)雜的表單交互邏輯。
這里我們需要注意的是 action 與 onSubmit 的區(qū)別。onSubmit 會(huì)優(yōu)先于 action 執(zhí)行。并且,如果我們?cè)?onSubmit 的回調(diào)函數(shù)中,使用了 preventDefault,action 回調(diào)將不會(huì)執(zhí)行。
function onSubmit(e) {
e.preventDefault()
// ...
}
在 onSubmit 中,我們可以結(jié)合 state,通過(guò)控制數(shù)據(jù)的行為來(lái)自定義表單行為,而無(wú)需過(guò)多依賴 HTML 表單元素本身的能力。具體如何抉擇大家需要根據(jù)自身項(xiàng)目的需求情況來(lái)定。