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

Implement useQuery Infinite Loading #819

Closed
flybayer opened this issue Apr 25, 2020 · 10 comments · Fixed by blitz-js/blitz#590
Closed

Implement useQuery Infinite Loading #819

flybayer opened this issue Apr 25, 2020 · 10 comments · Fixed by blitz-js/blitz#590

Comments

@flybayer
Copy link
Member

flybayer commented Apr 25, 2020

Blitz needs to export a useInfiniteQuery hook that's a thin wrapper on the useInfiniteQuery hook from react-query. This will be done very similar to how we are wrapping the useQuery hook.

  • Output of useInfiniteQuery should be fully typed
  • Add this to the example store app
import { useInfiniteQuery } from 'blitz'
import getProducts from '/app/products/queries/getProducts'

function Products(props) {
  const [
    groupedProducts,
    {
      isFetching,
      isFetchingMore,
      fetchMore,
      canFetchMore,
    }
  ] = useInfiniteQuery(
    getProducts, 
    (page = {first: 100, skip: 0}) => ({where: {storeId: props.storeId}, ...page}),
    {
      getFetchMore: (lastGroup, allGroups) => lastGroup.nextPage,
    }
  )

  return (
    <>
      {groupedProducts.map((group, i) => (
        <React.Fragment key={i}>
          {group.products.map(product => (
            <p key={product.id}>{product.name}</p>
          ))}
        </React.Fragment>
      ))}

      <div>
        <button
          onClick={() => fetchMore()}
          disabled={!canFetchMore || isFetchingMore}
        >
          {isFetchingMore
            ? 'Loading more...'
            : canFetchMore
            ? 'Load More'
            : 'Nothing more to load'}
        </button>
      </div>

      <div>{isFetching && !isFetchingMore ? 'Fetching...' : null}</div>
    </>
  )
}

And here's the query to work with that:

export default async function getProducts({ where, orderBy, first, skip }: GetProductsInput) {
  const products = await db.product.findMany({
    where,
    orderBy,
    first,
    skip,
  })

  const count = await db.product.count()
  const hasMore = skip < count
  const nextPage = hasMore ? { first, skip: skip + first } : null

  return {
    products,
    nextPage,
  }
}

Note: It's possible I've overlooked something critical in the above code, so is something doesn't seem right, it might not be right!

@eliashelleborn
Copy link
Contributor

Just wondering what's the reason for this not working the same as pagination? Can't we build this into useQuery aswell?

@flybayer
Copy link
Member Author

@eliasjohansson Good question — if you want to explore how we could do this, that'd be awesome!

@eliashelleborn
Copy link
Contributor

@flybayer Sure, ill have a go at it after work today.

@eliashelleborn
Copy link
Contributor

eliashelleborn commented Apr 28, 2020

Been playing with this for a while. First of all there seems to be a problem with the useInfiniteQuery types react-query#390. I had to have almost everything as any to even play around with this.

My biggest issue right now is prisma together with infinte query. Prisma does not provide any "pagination metadata" when using first, last, after, before. So it's hard to know if there is anything left to query and no super obvious way of telling it where to continue. I have got a basic example working OK, I can however not get canFetchMore & getFetchMore to work as intended due to the prisma issues above.

Also baking this into the useQuery function would maybe make it more of a mess than needed, especially when it comes to types.

@flybayer
Copy link
Member Author

Ok thanks for digging into this!

Let's keep it separate.

For canFetchMore, the query function will have to be modified to return this and anything else needed. Probably need two prisma calls in the query, one for .count and one for .findMany

@tsawan tsawan removed their assignment Apr 29, 2020
@flybayer
Copy link
Member Author

Let me know if anyone wants to work on this but you need more information about how to build it!

@eliashelleborn
Copy link
Contributor

I can quite decide what the API for this should look like. If we were to use getFetchMore as is in regular react-query it should be passed as a second parameter to the query function. In that case we need to make some changes in rpc.ts so that you can send more than just the params in the body.
Screen Shot 2020-05-18 at 21 11 53

We could also enforce so that the response from getFetchMore has to be a Partial of the query functions arguments (after, before etc.). That way we can keep the rpc as is and just add the getFetchMore response to the regular params.
Screen Shot 2020-05-18 at 21 17 11

@flybayer
Copy link
Member Author

flybayer commented May 21, 2020

@eliasjohansson sorry for the delay here.

I'm not quite following you on that 🤔

getFetchMore is one of the many react-query options which we accept via the third argument to our useQuery. I think this should work exactly as I have in the original post without any changes to the blitz query function signature.

I just added to the first post the implementation for the blitz query that should work with my useInfiniteQuery example.

@eliashelleborn
Copy link
Contributor

@flybayer Hmm, I think I might have been trying to use react-query too much as is. Normally the getFetchMore parameter would be sent through to the query function, and then you use it there to fetch depending on it. So in ur example we would only use the getFetchMore to tell the useInfiniteQuery if there is more to fetch or not. Will try that instead.

@flybayer
Copy link
Member Author

In my example the return result of getFetchMore does two things:

  1. tells react-query if there is more (if nextPage is truthy)
  2. returns the nextPage object that will be passed to the parameter function

Screen Shot 2020-05-22 at 20 22 04

This is exactly like the react-query useInfiniteQuery documentation, unless I'm missing something :)

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

Successfully merging a pull request may close this issue.

3 participants