Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SWRInfinte bug]: repeated api calls for initial page index #1638

Closed
VewMet opened this issue Nov 16, 2021 · 10 comments
Closed

[SWRInfinte bug]: repeated api calls for initial page index #1638

VewMet opened this issue Nov 16, 2021 · 10 comments

Comments

@VewMet
Copy link

VewMet commented Nov 16, 2021

In useSWRInfinite, our colleagues have spotted this issue that initial page is always requested repeatedly with any next page request.

Made an infinite scroller and console logged pageIndex, and url from the fetcher function. If you see attached screenshots below (in order), you can easily spot the pattern.

For every incremental page , it is making double requests - one for page 1 [initial page], and another for the next page. Changing revalidation rules in options has no effect.

image

image

image

image

image

image

Here's the custom hook used inside the scrolling div.

_

import { KeyLoader } from "swr";
import useSWRInfinite, { SWRInfiniteConfiguration } from "swr/infinite";
export const useFeedPagination = (
url: string,
options?: SWRInfiniteConfiguration
) => {
const PAGE_SIZE = 5;
const getKey: KeyLoader = (pageIndex: number, previousPageData: any[]) => {
console.log("key index ****", pageIndex);
pageIndex = pageIndex > 0 ? parseInt(previousPageData[0].postId) + 1 : 1;
if (previousPageData && !previousPageData.length) return null; // reached the end
return ${url}/posts/${pageIndex}/comments; // SWR key
};

const fetcher = (url: RequestInfo) => {
console.log("/////", url.toString());
if (url.toString().includes("/posts/1/")) {
// do
}
return fetch(url).then((res) => res.json());
};

const {
data,
size: page,
setSize: setPage,
error,
isValidating,
mutate,
} = useSWRInfinite(getKey, fetcher, options);

const paginatedData: T[] = [].concat.apply([], data || []);

const isLoadingMore = data && typeof data[page - 1] === "undefined";

const isReachedEnd = data && data[data.length - 1]?.length < PAGE_SIZE;
return {
mutate,
error,
paginatedData,
page,
setPage,
isLoadingMore,
isReachedEnd,
isValidating,
};
};

_

Originally posted by @VewMet in #1612 (comment)

@patriq
Copy link

patriq commented Nov 17, 2021

I am experiencing the same bug. I have tried using multiple versions (including the latest beta) with no luck.

Here is a quick simple example on how to reproduce it: https://codesandbox.io/s/festive-haze-2dtpo?file=/src/App.js

Notice in the console how when you press the load more button, you get 2 console prints representing each data fetch, one for the first page, and one for the current page.

Unless I am doing something wrong, I don't think this behaviour is expected.

@patriq
Copy link

patriq commented Nov 17, 2021

So I been testing versions below 1.0.0. I realized that from 1.0.0-beta.8 below (inclusive), useSWRInfinite works as intended. This was probably a change that was introduced in 1.0.0-beta.9 that introduced the unexpected behaviour of fetching first page on every load more click.

@VewMet
Copy link
Author

VewMet commented Nov 17, 2021

@patriq as can be seen from the test file changes here:

https://github.com/vercel/swr/pull/1301/files

Its clear that revalidation of the first page repeatedly is deliberately introduced. Since a lot of developers are finding this a "buggy" or rather unintended function, its better to keep the first page revalidation optional.

@patriq
Copy link

patriq commented Nov 17, 2021

Yeah I ended up finding that PR and realizing it was intended. I was looking at the code for a workaround but I am glad to know that you are considering the first page revalidation optional. Thank you very much for the quick reply.

@anonrig
Copy link

anonrig commented Nov 28, 2021

Hey, I've come to the masters for guidance. I'm experiencing the same issue and it's causing infinite state mutations where react later terminates the process. The 0th index is fetched multiple times. Any solutions in order to make useSWRInfinite worked as before?

ScreenShot 2021-11-28 at 17 01 55@2x

Here's a reproduced example on Socketkit. https://github.com/socketkit/socketkit/blob/main/services/web/components/table/table.js#L37

Just replace initialData with fallbackData (which was a bug I'm trying to solve atm)

@shuding
Copy link
Member

shuding commented Dec 12, 2021

Since SWR 1.1.0, there is a new option revalidateFirstPage that you can use to turn this off. Related docs: https://github.com/vercel/swr/releases/tag/1.1.0

If you are interested, here are the full discussions of why we built this feature in the first place: #1401 (comment), and why we now provide this new option to customize it.

Let me know if it helps you!

@anonrig
Copy link

anonrig commented Dec 15, 2021

Hi @shuding

Adding revalidateFirstPage still does not fix the issue. Introducing fallbackData still causes infinite loop (which was working beforehand). Here's the code: https://github.com/socketkit/socketkit/blob/main/services/web/components/table/table.jsx#L34

@shuding
Copy link
Member

shuding commented Dec 27, 2021

Hi @anonrig I took a quick look at your code but this effect seems suspicious, what does it do?

https://github.com/socketkit/socketkit/blob/ae9c80e576e02a3c3fd928046129840560e5fb98/services/web/components/table/table.jsx#L47

It changes the size to size + 1, and then it triggers again since size is in the dependencies.

@oste
Copy link

oste commented Mar 6, 2022

I am using useSWRInfinite with revalidateFirstPage set to false

  const {
    data,
    size,
    setSize,
  } = useSWRInfinite(getKey, fetcher, { revalidateFirstPage: false });

and using it along with a getStaticProps fallback like this

export const getStaticProps: GetStaticProps = async () => {
  const data = await fetcher(getData, {
    input: { count: 1 },
  });

  return {
    props: {
      fallback: data,
    },
  };
};

and then using the fallback in my page component:

    <SWRConfig
      value={{ fallback: { [unstable_serialize(getKey)]: [fallback] } }}>

I am noticing that the request waterfall requires the first page finishes loading before the second page can start fetching. I am wondering how I can prevent the first page refetch or at least allow it to run async with the next page request. This will provide a better UX since I am only wanting to load above the fold content initially and then fill in the remaining data as soon as possible. Waiting for a revalidation of the first page only slows this down.

I tried using revalidateAll: false and also tried things like useSWRConfig().cache.set(unstable_serialize(getKey), [fallback]) but neither seemed to help. @shuding maybe you have some ideas? Thanks.

@scaler-tauseef
Copy link

I am using useSWRInfinite with revalidateFirstPage set to false

  const {
    data,
    size,
    setSize,
  } = useSWRInfinite(getKey, fetcher, { revalidateFirstPage: false });

and using it along with a getStaticProps fallback like this

export const getStaticProps: GetStaticProps = async () => {
  const data = await fetcher(getData, {
    input: { count: 1 },
  });

  return {
    props: {
      fallback: data,
    },
  };
};

and then using the fallback in my page component:

    <SWRConfig
      value={{ fallback: { [unstable_serialize(getKey)]: [fallback] } }}>

I am noticing that the request waterfall requires the first page finishes loading before the second page can start fetching. I am wondering how I can prevent the first page refetch or at least allow it to run async with the next page request. This will provide a better UX since I am only wanting to load above the fold content initially and then fill in the remaining data as soon as possible. Waiting for a revalidation of the first page only slows this down.

I tried using revalidateAll: false and also tried things like useSWRConfig().cache.set(unstable_serialize(getKey), [fallback]) but neither seemed to help. @shuding maybe you have some ideas? Thanks.

We are also facing this issue, i.e revalidateFirstPage when well with client fetch. The moment we add in fallback of getStaticProps it again start fetching the first page

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants