useFetchers
傳回一個陣列,其中包含所有飛行中的 fetchers,不包含它們的 load
、submit
或 Form
屬性(無法讓父層元件試圖控制子層元件的行為!我們從現實生活中知道這根本就是徒勞無功。)
import { useFetchers } from "react-router-dom";
function SomeComp() {
const fetchers = useFetchers();
// array of inflight fetchers
}
這對應用程式中沒有建立 fetchers 但想使用其提交內容參與樂觀 UI 的元件很有用。
例如,想像一個使用者介面,其中側邊欄列出專案,而主畫面顯示目前專案核取方塊的清單。側邊欄可以顯示每個專案完成的任務數和總任務數。
+-----------------+----------------------------+
| | |
| Soccer (8/9) | [x] Do the dishes |
| | |
| > Home (2/4) | [x] Fold laundry |
| | |
| | [ ] Replace battery in the |
| | smoke alarm |
| | |
| | [ ] Change lights in kids |
| | bathroom |
| | |
+-----------------+----------------------------┘
當使用者點選核取方塊時,提交內容會傳送至動作以變更任務的狀態。我們不想要建立「載入狀態」,而是想要建立一個「樂觀 UI」,即使伺服器尚未處理核取方塊,也會立即更新核取方塊以顯示已勾選。在核取方塊元件中,我們可以使用 fetcher.formData
function Task({ task }) {
const { projectId, id } = task;
const toggle = useFetcher();
const checked = toggle.formData
? toggle.formData.get("complete") === "on"
: task.complete;
return (
<toggle.Form
method="put"
action={`/projects/${projectId}/tasks/${id}`}
>
<input name="id" type="hidden" defaultValue={id} />
<label>
<input
name="complete"
type="checkbox"
checked={checked}
onChange={(e) => toggle.submit(e.target.form)}
/>
</label>
</toggle.Form>
);
}
這對核取方塊而言很棒,但當使用者點選其中一個核取方塊時,側邊欄會顯示 2/4 而核取方塊會顯示 3/4!
+-----------------+----------------------------+
| | |
| Soccer (8/9) | [x] Do the dishes |
| | |
| > Home (2/4) | [x] Fold laundry |
| WRONG! ^ | |
| CLICK!-->[x] Replace battery in the |
| | smoke alarm |
| | |
| | [ ] Change lights in kids |
| | bathroom |
| | |
+-----------------+----------------------------┘
由於路由會自動重新驗證,因此側邊欄會迅速更新並正確無誤。但是,在短暫的時間內,它會有點怪怪的。
這是 useFetchers
出場的地方。在側邊欄中,我們可以透過檢查方塊來存取所有傳送中的 fetcher 狀態 - 即使它不是建立這些狀態的元件。
策略有三個步驟
fetcher.formData
立即更新次數function ProjectTaskCount({ project }) {
let completedTasks = 0;
const fetchers = useFetchers();
// Find this project's fetchers
const relevantFetchers = fetchers.filter((fetcher) => {
return fetcher.formAction?.startsWith(
`/projects/${project.id}/tasks/`
);
});
// Store in a map for easy lookup
const myFetchers = new Map(
relevantFetchers.map(({ formData }) => [
formData.get("id"),
formData.get("complete") === "on",
])
);
// Increment the count
for (const task of project.tasks) {
if (myFetchers.has(task.id)) {
if (myFetchers.get(task.id)) {
// if it's being submitted, increment optimistically
completedTasks++;
}
} else if (task.complete) {
// otherwise use the real task's data
completedTasks++;
}
}
return (
<small>
{completedTasks}/{project.tasks.length}
</small>
);
}
這需要花一些功夫,但大部分只是要求 React Router 提供它追蹤中的狀態,並根據狀態進行樂觀計算。