搜尋
首頁web前端css教學懸念:加載數據時學到的教訓

React Suspense: 從數據加載中汲取的經驗教訓

React Suspense: Lessons Learned While Loading Data

Suspense是React即將推出的一個功能,它有助於協調異步操作(例如數據加載),使您可以輕鬆防止UI中的狀態不一致。我將對這到底意味著什麼進行更詳細的解釋,並簡要介紹Suspense,然後介紹一個比較現實的用例,並介紹一些經驗教訓。

我介紹的功能仍處於alpha階段,絕不能用於生產環境。這篇文章是為那些想要搶先體驗即將推出的功能並了解未來發展方向的人準備的。

Suspense入門

應用程序開發中最具挑戰性的部分之一是協調應用程序狀態和數據加載方式。狀態更改通常會在多個位置觸發新的數據加載。通常,每條數據都會有自己的加載UI(例如“旋轉器”),大致位於數據在應用程序中的位置。數據加載的異步特性意味著這些請求可以以任何順序返回。結果,您的應用程序不僅會出現許多不同的旋轉器出現和消失,更糟糕的是,您的應用程序可能會顯示不一致的數據。如果您的三個數據加載中有兩個已完成,那麼您將看到一個加載旋轉器位於第三個位置的頂部,仍然顯示舊的、現在已過時的數據。

我知道這太多了。如果您覺得其中任何內容令人費解,您可能對之前我撰寫的一篇關於Suspense的文章感興趣。該文章更詳細地介紹了Suspense是什麼以及它實現了什麼。請注意,其中一些小細節現在已經過時了,即useTransition鉤子不再接受timeoutMs值,而是無限期等待。

現在,讓我們快速瀏覽一下細節,然後進入一個具體的用例,其中有一些潛伏的陷阱。

Suspense的工作原理

幸運的是,React團隊足夠聰明,沒有將這些努力限制在僅僅加載數據上。 Suspense通過低級基元工作,您可以將其應用於幾乎任何事物。讓我們快速瀏覽一下這些基元。

首先是<suspense></suspense>邊界,它接受一個fallback屬性:

<suspense fallback="{<Fallback"></suspense> }>

每當此組件下的任何子組件掛起時,它都會呈現fallback 。無論有多少子組件掛起,無論出於何種原因,顯示的都是fallback 。這是React確保UI一致的一種方式——在所有內容準備就緒之前,它不會呈現任何內容。

但是,在內容最初呈現之後,用戶更改狀態並加載新數據會怎樣呢?我們當然不希望我們現有的UI消失並顯示我們的fallback ;那將是糟糕的用戶體驗。相反,我們可能希望顯示一個加載旋轉器,直到所有數據準備就緒,然後才顯示新的UI。

useTransition鉤子實現了這一點。此鉤子返回一個函數和一個布爾值。我們調用該函數並包裝我們的狀態更改。現在事情變得有趣了。 React嘗試應用我們的狀態更改。如果任何內容掛起,React會將該布爾值設置為true ,然後等待掛起結束。完成後,它將嘗試再次應用狀態更改。也許這次它會成功,或者也許其他內容會掛起。無論如何,布爾標誌都會保持為true ,直到所有內容都準備就緒,並且只有在那時,狀態更改才會完成並反映在UI中。

最後,我們如何掛起?我們通過拋出一個promise來掛起。如果請求數據並且我們需要獲取,那麼我們獲取——並拋出一個與該獲取相關的promise。這種低級別的掛起機制意味著我們可以將其用於任何事物。用於延遲加載組件的React.lazy實用程序已經與Suspense一起工作,我之前已經寫過關於使用Suspense在顯示UI之前等待圖像加載以防止內容移動的文章。

別擔心,我們會討論所有這些。

我們正在構建什麼

我們將構建一些與其他類似文章中的示例略有不同的事物。請記住,Suspense仍在alpha階段,因此您最喜歡的加載數據實用程序可能還沒有Suspense支持。但這並不意味著我們不能偽造一些東西並了解Suspense的工作原理。

讓我們構建一個無限加載列表,該列表顯示一些數據,並結合一些基於Suspense的預加載圖像。我們將顯示我們的數據,以及一個加載更多數據的按鈕。當數據呈現時,我們將預加載關聯的圖像,並在準備就緒之前掛起。

此用例基於我在我的副項目中完成的實際工作(再次,不要在生產環境中使用Suspense——但副項目是允許的)。我當時正在使用我自己的GraphQL客戶端,這篇文章的動機是我遇到的一些困難。為了簡化操作並專注於Suspense本身,而不是任何單個數據加載實用程序,我們將只偽造數據加載。

開始構建!

這是我們初始嘗試的沙箱。我們將使用它來逐步講解所有內容,因此現在不必急於理解所有代碼。

我們的根App組件呈現一個像這樣的Suspense邊界:

<suspense fallback="{<Fallback"></suspense> }>

每當任何內容掛起(除非狀態更改發生在useTransition調用中), fallback都會呈現。為了使事情更容易理解,我使這個Fallback組件將整個UI變成粉紅色,這樣就很難錯過;我們的目標是理解Suspense,而不是構建高質量的UI。

我們正在DataList組件內加載當前的數據塊:

 const newData = useQuery(param);

我們的useQuery鉤子被硬編碼為返回偽造數據,包括模擬網絡請求的超時。它處理緩存結果,如果數據尚未緩存,則拋出一個promise。

我們(至少目前)將狀態保存在我們正在顯示的主數據列表中:

 const [data, setData] = useState([]);

當新的數據從我們的鉤子傳入時,我們將其附加到我們的主列表中:

 useEffect(() => {
  setData((d) => d.concat(newData));
}, [newData]);

最後,當用戶需要更多數據時,他們單擊該按鈕,該按鈕會調用此函數:

 function loadMore() {
  startTransition(() => {
    setParam((x) => x 1);
  });
}

最後,請注意,我正在使用SuspenseImg組件來處理我正在與每條數據一起顯示的圖像的預加載。只顯示五張隨機圖像,但我添加了一個查詢字符串以確保為我們遇到的每條新數據進行新的加載。

總結

為了總結我們目前所處的位置,我們有一個加載當前數據的鉤子。該鉤子遵守Suspense機制,並在加載發生時拋出一個promise。每當該數據更改時,正在運行的項目總列表都會更新並附加新項目。這發生在useEffect中。每個項目都呈現一個圖像,我們使用SuspenseImg組件來預加載圖像,並在準備就緒之前掛起。如果您好奇某些代碼的工作原理,請查看我之前關於使用Suspense預加載圖像的文章。

讓我們測試一下

如果一切正常,這將是一篇非常無聊的博客文章,別擔心,它不正常。請注意,在初始加載時,粉紅色的fallback屏幕會顯示然後快速隱藏,但隨後會重新顯示。

當我們單擊加載更多數據的按鈕時,我們會看到內聯加載指示器(由useTransition鉤子控制)翻轉為true 。然後我們看到它翻轉為false ,然後我們的原始粉紅色fallback顯示。我們期望在初始加載後不再看到那個粉紅色屏幕;內聯加載指示器應該顯示直到所有內容都準備就緒。發生了什麼?

問題

它一直隱藏在顯眼之處:

 useEffect(() => {
  setData((d) => d.concat(newData));
}, [newData]);

useEffect在狀態更改完成時運行,即狀態更改已完成掛起,並已應用於DOM。那部分,“已完成掛起”是這裡的關鍵。如果我們願意,我們可以在此處設置狀態,但是如果該狀態更改再次掛起,則這是一個全新的掛起。這就是為什麼我們在初始加載以及隨後數據加載完成後看到粉紅色閃爍的原因。在這兩種情況下,數據加載都已完成,然後我們在一個效果中設置狀態,這導致新數據實際呈現並再次掛起,因為圖像預加載。

那麼,我們如何解決這個問題呢?在一個層面上,解決方案很簡單:停止在效果中設置狀態。但這說起來容易做起來難。我們如何在不使用效果的情況下更新正在運行的條目列表以附加新的結果?您可能認為我們可以使用ref來跟踪事物。

不幸的是,Suspense帶來了一些關於ref的新規則,即我們不能在渲染內部設置ref。如果您想知道為什麼,請記住Suspense完全是關於React嘗試運行渲染,看到promise被拋出,然後在中途丟棄該渲染。如果我們在渲染被取消和丟棄之前更改了ref,則ref仍然具有該更改,但無效的值。渲染函數需要是純的,沒有副作用。這始終是React的一條規則,但現在它更重要了。

重新思考我們的數據加載

這是解決方案,我們將逐段講解。

首先,與其將我們的主數據列表存儲在狀態中,不如做些不同的事情:讓我們存儲我們正在查看的頁面列表。我們可以將最近的頁面存儲在ref中(儘管我們不會在渲染中寫入它),並將所有當前加載的頁面的數組存儲在狀態中。

 const currentPage = useRef(0);
const [pages, setPages] = useState([currentPage.current]);

為了加載更多數據,我們將相應地更新:

 function loadMore() {
  startTransition(() => {
    currentPage.current = currentPage.current 1;
    setPages((pages) => pages.concat(currentPage.current));
  });
}

然而,棘手的部分是如何將這些頁碼轉換為實際數據。我們當然不能做的是循環遍歷這些頁面並調用我們的useQuery鉤子;鉤子不能在循環中調用。我們需要一個新的、非基於鉤子的數據API。根據我在過去的Suspense演示中看到的非常非官方的約定,我將此方法命名為read() 。它不會是一個鉤子。如果數據被緩存,它會返回請求的數據,否則會拋出一個promise。對於我們的偽造數據加載鉤子,沒有必要進行任何真正的更改;我只是簡單地複制粘貼了鉤子,然後將其重命名。但是對於實際的數據加載實用程序庫,作者可能需要做一些工作才能將這兩個選項都作為其公共API的一部分公開。在我前面提到的GraphQL客戶端中,確實同時存在useSuspenseQuery鉤子和客戶端對像上的read()方法。

有了這個新的read()方法,我們代碼的最後部分就微不足道了:

 const data = pages.flatMap((page) => read(page));

我們獲取每個頁面,並使用我們的read()方法請求相應的數據。如果任何頁面未被緩存(實際上應該只有列表中的最後一個頁面),則會拋出一個promise,React會為我們掛起。當promise解析時,React會再次嘗試之前的狀態更改,這段代碼也會再次運行。

不要讓flatMap調用迷惑你。它與map做的事情完全相同,只是它獲取新數組中的每個結果,如果它本身是一個數組,則將其“展平”。

結果

通過這些更改,當我們開始時,一切都會按預期工作。我們的粉紅色加載屏幕在初始加載時顯示一次,然後在後續加載中,內聯加載狀態顯示直到所有內容都準備就緒。

結束語

Suspense是對React的令人興奮的更新。它仍處於alpha階段,因此不要嘗試將其用於任何重要的事情。但是,如果您是那種喜歡搶先體驗即將推出的內容的開發人員,那麼我希望這篇文章為您提供了一些有用的背景信息和信息,這些信息在發佈時會有用。

以上是懸念:加載數據時學到的教訓的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
模擬鼠標運動模擬鼠標運動Apr 22, 2025 am 11:45 AM

如果您曾經在現場演講或課程中必須顯示一個互動動畫,那麼您可能知道它並不總是那麼容易與您的幻燈片進行互動

通過Astro Action和Fuse.js為搜索提供動力通過Astro Action和Fuse.js為搜索提供動力Apr 22, 2025 am 11:41 AM

對於Astro,我們可以在構建過程中生成大部分網站,但是有一小部分服務器端代碼可以使用Fuse.js之類的搜索功能來處理搜索功能。在此演示中,我們將使用保險絲搜索一組個人“書籤”

未定義:第三個布爾值未定義:第三個布爾值Apr 22, 2025 am 11:38 AM

我想在我的一個項目中實現一條通知消息,類似於您在保存文檔時在Google文檔中看到的信息。換句話說,一個

捍衛三元聲明捍衛三元聲明Apr 22, 2025 am 11:25 AM

幾個月前,我正在使用黑客新聞(就像一個人一樣),並且遇到了一篇(現已刪除的)文章,內容涉及不使用if語句。如果您是這個想法的新手(就像我

使用網絡語音API進行多語言翻譯使用網絡語音API進行多語言翻譯Apr 22, 2025 am 11:23 AM

自科幻小說以來,我們就幻想著與我們交談的機器。今天這很普遍。即便如此,製造的技術

JetPack Gutenberg塊JetPack Gutenberg塊Apr 22, 2025 am 11:20 AM

我記得當古騰堡被釋放到核心時,因為那天我在WordCamp我們。現在已經過去了幾個月,所以我想我們越來越多的人

在VUE中創建可重複使用的分頁組件在VUE中創建可重複使用的分頁組件Apr 22, 2025 am 11:17 AM

大多數Web應用程序背後的想法是從數據庫中獲取數據,並以最佳方式將其呈現給用戶。當我們處理數據時

使用'盒子陰影”和剪輯路徑一起使用'盒子陰影”和剪輯路徑一起Apr 22, 2025 am 11:13 AM

讓我們在一個情況下做一些似乎有意義的事情的情況下逐步進行一些逐步,但是您仍然可以用CSS欺騙來完成它。在這個

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器