React Router V6 - Form & Action
上一篇介紹了 React Router V6 的 Loader,本文會介紹另一個重要的新功能 Action 與表單處理方式。
Use action() to Send Form Data
在 React 中,如果要提交表單,我們通常不會採取原生的提交方式,而是利用 State 去管理表單資料,並建立 Function 去串接 API 以送出表單,可能還會加上 Loading 效果、處理 Error 與 Redirect 等等。
React Router V6 提供的 <Form>
元件會阻止原生的表單行為,路由會在表單送出後執行 action
的內容,以下是一個簡單的範例。
1import { Form } from 'react-router-dom'; 2 3const EventForm = () => { 4 return ( 5 <Form method="post"> 6 <label htmlFor="title">Title</label> 7 <input id="title" type="text" name="title" /> 8 </Form> 9 ); 10}; 11 12export default EventForm; 13
在使用 React Router 的 <Form>
時,記得要為 Form Elements 加上 name
這個屬性,方便後續取得該欄位的值。
接著,我們建立 action
來做送出表單的行為,Action 與 Loader 一樣都有 request
與 params
這兩個參數。
我們可以使用 request
提供的 formData()
方法,取得使用者提交的表單資料,再透過原生 FormData 提供的 get(name)
方法,就能取得對應 name
屬性的 value
囉。
1export const action = async ({ request, params }) => { 2 const data = await request.formData(); 3 4 const eventData = { 5 title: data.get('title'), // get("title") 對應到 name="title" 的 input 6 image: data.get('image'), 7 date: data.get('date'), 8 description: data.get('description'), 9 }; 10 11 const response = await fetch('http://localhost:8080/events', { 12 method: 'POST', 13 headers: { 14 'Content-Type': 'application/json', 15 }, 16 body: JSON.stringify(eventData), 17 }); 18 19 if (!response.ok) { 20 throw json({ message: 'Could not send event' }, { status: 500 }); 21 } 22 23 return redirect('/events'); 24}; 25
最後我們使用了 React Router 提供的 redirect()
函式來進行重新導頁。
當判斷已成功取得回應時,我們傳入目標路由作為該函式的參數,例如:執行 return redirect("/events")
讓 Router 導回活動列表頁面。
使用 useSubmit 程式化提交表單
結合 react-router-dom
提供的 useSubmit
Hook 以及 action()
函式,我們也可以不使用 <Form>
元件,直接以程式化的方式去執行表單的送出與後續處理。
以下範例中,我們希望用程式化的方式執行刪除功能,而非透過送出表單的方式執行。
首先,將刪除按鈕綁上 startDeleteHandler
函式,這個函式會在執行後出現 Confirm 提示,一旦確認後就會執行 useSubmit
Hook 的功能。
接著執行的 submit()
函式可以放入兩個參數,第一個參數是 FormData 的物件,第二個參數是 Form 表單元素的 Properties。
1import { useSubmit } from 'react-router-dom'; 2 3const EventItem = ({ event }) => { 4 const submit = useSubmit(); 5 6 const startDeleteHandler = () => { 7 const proceed = window.confirm('Are you sure you want to delete'); 8 9 if (proceed) { 10 // params: FormData Object, Form Properties 11 submit(null, { method: 'delete' }); 12 } 13 }; 14 15 return ( 16 <article> 17 {/* ... */} 18 <button onClick={startDeleteHandler}>Delete</button> 19 </article> 20 ); 21}; 22 23export default EventItem; 24
使用 useSubmit
就跟送出表單一樣,都會去觸發路由裡面定義的 action()
,所以我們要在負責執行刪除行為的元件中定義 action()
的內容。
當 useSubmit
觸發時,路由會執行 action()
去呼叫執行刪除動作的 API,並且 useSubmit
所定義的參數也會傳入 request
當中。
例如:撰寫 fetch()
的 method
時,可以使用傳入的 request.method
。
1export const action = async ({ request, params }) => { 2 const eventId = params.eventId; 3 4 const response = await fetch(`http://localhost:8080/events/${eventId}`, { 5 method: request.method, // submit(null, { method: "delete" }); 6 }); 7 8 if (!response.ok) { 9 throw json({ message: 'Could not delete event' }, { status: 500 }); 10 } 11 12 return redirect('/events'); 13}; 14
這樣就能順利以程式化的方式執行刪除動作囉!
回顧
看完這篇文章,我們認識了 React Router V6 新增的 Action 與 Form 元件。
除此之外還有許多實用的功能,建議可以看看他們的官方文件,這次更新後文件整理得很不錯。