Skip to content

Better documentation on the revalidation behavior of the infinite hook #785

Open
@grazianodev

Description

@grazianodev

Bug report

Description / Observed Behavior

I have created a simple component that fetches a list of posts via GraphQL requests (cursor-based) using useSWRInfinite. Apparently everything works fine, i.e. the pages are all fetched correctly, in the correct order, etc. but when inspecting the 'Network' tab I can see that every time I load a new page, the first page is also fetched again. So, when I click 'Load more' two requests are made, one for the next page, the other for the first page (unnecessary).

I'm reporting it as a bug because I've seen it happen with the official infinite loading example, although only occasionally (in my app it always happens). I think the problem has something to do with the fact that the 'index' that is passed to 'getKey' correctly increases by 1 when loading more, but on re-render it resets to 0 (this always happen in the official example, too), so when the component re-renders and executes 'getKey' it fetches the first page again because 'index' is 0 again.

Expected Behavior

I would expect that only the request for the next page is made.

Repro Steps / Code Example

Here's my component:


// Index.js 

const Index = ( { posts } ) => {	

  const getKey = ( index, previousData ) => {

    console.log( "index", index ) // 'index' increases by 1 after loading more, then resets to 0
    if( index === 0 ) return [ `/libs/api/posts`, "" ]
    
    return [ `/libs/api/posts`, previousData?.pageInfo?.endCursor ]
  
  }

  const { 
    data, error, mutate, size, setSize, isValidating 
  } = useSWRInfinite( getKey, getAllPosts, { initialData: [ posts ] } )

  return(
    <div>
      <LogInOutLink />
      {
        data.map( ( page, index ) => {
          return(
            <div key={ index }>
              <h3>Page n. { index }</h3>
              <ul>{ page.edges.map( ( { node } ) => <li key={ node.id }>{ node.title }</li> ) }</ul>
            </div>
          )
        })
      }
      <button onClick={ () => setSize( size + 1 ) }>Load more</button>
    </div>
  )	

}

export const getStaticProps = async () => {

  const posts = await getAllPosts( "/libs/api/posts" )

  return { props: { posts } }

}

export default Index

And here's my fetcher:

const getAllPosts = async ( key, cursor = "" ) => {
   
   const query = `
      query getPosts( 
         $after: String, 
         $first: Int = 8 	
      ) {
         postsRoot: contentNodes(	
            after: $after, 
            first: $first, 
            where: { contentTypes: [ POST, NEWS ] } 
         ) {
            ${PAGINATION_FIELDS}
            ${POSTS_LIST_FIELDS}
         }
      }
   `
   
   const variables = cursor ? { after: cursor } : {}

   const data = await fetchAPI( query, variables )
 
   return data?.postsRoot

}

export default getAllPosts
export const fetchAPI = async ( query, variables = {} ) => {
 
   const res = await fetch( `${process.env.NEXT_PUBLIC_BACKEND_BASE_URL}/graphql`, {
      method: "POST",
      headers: {
         "Content-Type": "application/json"
      },
      body: JSON.stringify({
         query,
         variables
      })
   })
 
   const json = await res.json()
   
   if( json.errors ) {
     console.error( json.errors )
     throw new Error('Failed to fetch API')
   }
   
   return json.data

}

Additional Context

I encountered the problem with the latest version (0.3.9), so I also tried with 0.3.0 but to no luck.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions