From d1b7169adf01feaf47f46c556208770680680f6f Mon Sep 17 00:00:00 2001
From: Yixuan Xu <yixuanxu94@outlook.com>
Date: Mon, 31 Jul 2023 21:27:20 +0800
Subject: [PATCH] fix: preload request should be consumed within `revalidate`
 to support `parallel` option (#2727)

* fix: preload request should be consumed within revalidate.

* adjust order

* update test comment
---
 infinite/src/index.ts                  | 26 +++++++--------
 test/use-swr-infinite-preload.test.tsx | 45 ++++++++++++++++++++++++++
 2 files changed, 56 insertions(+), 15 deletions(-)

diff --git a/infinite/src/index.ts b/infinite/src/index.ts
index 2b41b1c78..b31bbc0b7 100644
--- a/infinite/src/index.ts
+++ b/infinite/src/index.ts
@@ -169,22 +169,8 @@ export const infinite = (<Data, Error>(useSWRNext: SWRHook) =>
             SWRInfiniteCacheValue<Data, any>
           >(cache, pageKey)
 
-          const hasPreloadedRequest = pageKey in PRELOAD
           // Get the cached page data.
           let pageData = getSWRCache().data as Data
-
-          if (hasPreloadedRequest) {
-            const req = PRELOAD[pageKey]
-            // delete the preload cache key before resolving it
-            // in case there's an error
-            delete PRELOAD[pageKey]
-            // get the page data from the preload cache
-            pageData = await req
-            // set the SWR cache with the preloaded data
-            setSWRCache({ data: pageData, _k: pageArg })
-            // remove the preload cache key to prevent memory leak
-          }
-
           // should fetch (or revalidate) if:
           // - `revalidateAll` is enabled
           // - `mutate()` called
@@ -203,7 +189,17 @@ export const infinite = (<Data, Error>(useSWRNext: SWRHook) =>
               !config.compare(cacheData[i], pageData))
           if (fn && shouldFetchPage) {
             const revalidate = async () => {
-              pageData = await fn(pageArg)
+              const hasPreloadedRequest = pageKey in PRELOAD
+              if (!hasPreloadedRequest) {
+                pageData = await fn(pageArg)
+              } else {
+                const req = PRELOAD[pageKey]
+                // delete the preload cache key before resolving it
+                // in case there's an error
+                delete PRELOAD[pageKey]
+                // get the page data from the preload cache
+                pageData = await req
+              }
               setSWRCache({ data: pageData, _k: pageArg })
               data[i] = pageData
             }
diff --git a/test/use-swr-infinite-preload.test.tsx b/test/use-swr-infinite-preload.test.tsx
index c25d8fbc3..2dadb694f 100644
--- a/test/use-swr-infinite-preload.test.tsx
+++ b/test/use-swr-infinite-preload.test.tsx
@@ -251,4 +251,49 @@ describe('useSWRInfinite - preload', () => {
     preload(() => getKey(0), fetcher)
     expect(calledWith).toBe(getKey(0))
   })
+  it('should not break parallel option', async () => {
+    // mock api
+    const pageData = ['apple', 'banana', 'pineapple']
+
+    const key = createKey()
+    const fetcher = ([_, index]) =>
+      createResponse(`${pageData[index]}, `, { delay: index === 0 ? 50 : 200 })
+    function Page() {
+      const { data } = useSWRInfinite(index => [key, index], fetcher, {
+        initialSize: 3,
+        parallel: true
+      })
+
+      return <div>data:{data}</div>
+    }
+    preload([key, 0], fetcher)
+    renderWithConfig(<Page />)
+    screen.getByText('data:')
+    // If SWR sends parallel requests, it should only take 200ms
+    await act(() => sleep(200))
+    screen.getByText('data:apple, banana, pineapple,')
+  })
+  it('should be able to preload multiple page', async () => {
+    // mock api
+    const pageData = ['apple', 'banana', 'pineapple']
+
+    const key = createKey()
+    const fetcher = ([_, index]) =>
+      createResponse(`${pageData[index]}, `, { delay: 50 })
+    function Page() {
+      const { data } = useSWRInfinite(index => [key, index], fetcher, {
+        initialSize: 3,
+        parallel: true
+      })
+
+      return <div>data:{data}</div>
+    }
+    preload([key, 0], fetcher)
+    preload([key, 1], fetcher)
+    preload([key, 2], fetcher)
+    renderWithConfig(<Page />)
+    screen.getByText('data:')
+    await act(() => sleep(50))
+    screen.getByText('data:apple, banana, pineapple,')
+  })
 })