Open FelixZY opened 3 months ago
For now, it is possible to work around the issue using this wrapper function:
import { useCallback } from "react";
import useSWRInfinite, { SWRInfiniteHook } from "swr/dist/infinite";
export const useSWRInfinitePatched: SWRInfiniteHook =
function useSWRInfinitePatched(this: unknown, ...params: unknown[]) {
const hook = useSWRInfinite.apply(this, params as any);
const originalSetSize = hook.setSize;
hook.setSize = useCallback(
(sizeOrFn) => {
const newSize =
typeof sizeOrFn === "function" ? sizeOrFn(hook.size) : sizeOrFn;
// Patch for
// https://github.com/vercel/swr/issues/3001
if (newSize === hook.size) {
// Based on
// https://github.com/vercel/swr/blob/1585a3e37d90ad0df8097b099db38f1afb43c95d/src/infinite/index.ts#L295
return Promise.resolve() as Promise<undefined>;
} else {
return originalSetSize(newSize);
}
},
[originalSetSize, hook.size]
);
return hook;
} as unknown as SWRInfiniteHook;
hello! i'm working on an email app with infinite loading and was struggling to figure out why our api was getting pinged multiple times on fast scroll scenarios. in our case, we have optimistic ui that was loading and increasing the size of the infinite loader, but the api wasn't finished with a subsequent api call.
the crux of the issue is that the useSWRInfinite is playing "catch up" with our optimistic ui. for example, the optimisic ui would set the size to 10, but the api would be still loading the 2nd page of data.
I believe that this probably goes against the use case of useSWRInfinite, where one page should theoretically be loaded before another page can even start loading, and I think this is apparent in the source code:
https://github.com/vercel/swr/blob/main/src/infinite/index.ts#L184
in the "catch up scenario", the 10th call of the fetcher function notices that the pages 2-10 need loading, so it will trigger an api request for pages 2-10, but the 9th call of the fetcher function notices that pages 2-9 need loading so it will trigger an additional api request for pages 2-9, etc.
I figured that this probably won't be fixed with SWR (since it goes against default behavior), and I also have a workaround solution that uses a local size & setSize function + a useEffect to follow the incremental page increases without triggering useSWRInfinite's default functionality for loading the entirety of the undefined dataset
hopefully this helps too!
const { size, setSize, data } = useSWRInfinite(...)
const [localSize, setLocalSize] = useState(1)
// my pagination function uses setLocalSize instead of setSize
useEffect(() => {
if (localSize > size) {
const isFullyLoaded = data?.length === size
if (isFullyLoaded) {
setSize(size + 1)
}
}
}, [localSize, size, data?.length])
Bug report
Description / Observed Behavior
In an infinite scroll scenario,
useSWRInfinite
would trigger tens of duplicated requests in the span of a few milliseconds.Expected Behavior
SWR should have triggered only one request
Repro Steps / Code Example
Minimal reproduction set up here: https://github.com/FelixZY/swr-setsize-bug-repro
Using the linked reproduction:
npm start
Expected:
The console output should show
Actual:
Actual output (example)
``` /0: 1 /1: 1 /1: 2 /1: 3 /1: 4 /1: 5 /1: 6 /1: 7 /1: 8 /1: 9 /1: 10 /1: 11 /2: 1 /2: 2 /2: 3 /2: 4 /2: 5 /2: 6 /2: 7 /2: 8 /2: 9 /2: 10 /2: 11 /2: 12 /2: 13 /2: 14 /2: 15 /2: 16 /3: 1 /3: 2 /3: 3 /3: 4 /3: 5 /3: 6 /3: 7 /3: 8 /3: 9 /3: 10 /3: 11 /3: 12 /3: 13 /3: 14 /3: 15 /3: 16 /3: 17 /3: 18 /3: 19 /4: 1 /4: 2 /4: 3 /4: 4 /4: 5 /4: 6 /4: 7 /4: 8 /4: 9 /4: 10 /4: 11 /4: 12 /4: 13 /4: 14 /4: 15 /4: 16 /4: 17 /4: 18 /4: 19 /4: 20 /5: 1 /5: 2 /5: 3 /5: 4 /5: 5 /5: 6 /5: 7 /5: 8 /5: 9 /5: 10 /5: 11 /5: 12 /5: 13 /5: 14 /5: 15 /5: 16 /5: 17 /5: 18 /5: 19 /5: 20 /6: 1 /6: 2 /6: 3 /6: 4 /6: 5 /6: 6 /6: 7 /6: 8 /6: 9 /6: 10 /6: 11 /6: 12 /6: 13 /6: 14 /6: 15 /6: 16 /6: 17 /6: 18 /6: 19 /6: 20 /7: 1 /7: 2 /7: 3 /7: 4 /7: 5 /7: 6 /7: 7 /7: 8 /7: 9 /7: 10 /7: 11 /7: 12 /7: 13 /7: 14 /7: 15 /7: 16 /7: 17 /7: 18 /7: 19 /7: 20 /8: 1 /8: 2 /8: 3 /8: 4 /8: 5 /8: 6 /8: 7 /8: 8 /8: 9 /8: 10 /8: 11 /8: 12 /8: 13 /8: 14 /8: 15 /8: 16 /8: 17 /8: 18 /8: 19 /8: 20 /9: 1 /9: 2 /9: 3 /9: 4 /9: 5 /9: 6 /9: 7 /9: 8 /9: 9 /9: 10 /9: 11 /9: 12 /9: 13 /9: 14 /9: 15 /9: 16 /9: 17 /9: 18 /9: 19 /9: 20 ```Screencast from 2024-08-08 13:31:48.webm
Additional Context
SWR version: 2.2.5
It seems the crucial step is for
setSize
to be called multiple times before loading has actually finished. You can confirm this by removingdelay
here:https://github.com/FelixZY/swr-setsize-bug-repro/blob/22ae6eb8181b227739fd0512444501a3a14bb3d6/src/App.js#L20