Repository: tannerlinsley/react-query
Pull request #822 (2020-08-02 01:50:56)

fix(useInfiniteQuery): correctly set canFetchMore
canFetchMore should be false for falsy getFetchMore return value

vercel[bot]: This pull request is being automatically deployed with Vercel ([learn more](https://vercel.link/github-learn-more)).
To see the status of your deployment, click on the icon next to each commit.

Patch:
diff --git a/src/core/query.ts b/src/core/query.ts
index e9fe36011..7dee1df88 100644
--- a/src/core/query.ts
+++ b/src/core/query.ts
@@ -154,10 +154,10 @@ export class Query<TResult, TError> {
           infiniteData[infiniteData.length - 1],
           infiniteData
         )
-        this.state.canFetchMore = this.fetchMoreVariable !== false
+        this.state.canFetchMore = Boolean(this.fetchMoreVariable)
       }
 
-      // Here we seed the pageVariabes for the query
+      // Here we seed the pageVariables for the query
       if (!this.pageVariables) {
         this.pageVariables = [[...this.queryKey]]
       }
@@ -460,7 +460,7 @@ export class Query<TResult, TError> {
           data[data.length - 1],
           data
         )
-        this.state.canFetchMore = this.fetchMoreVariable !== false
+        this.state.canFetchMore = Boolean(this.fetchMoreVariable)
         this.pageVariables = rebuiltPageVariables
 
         return (data as unknown) as TResult
@@ -497,7 +497,7 @@ export class Query<TResult, TError> {
             }
 
             this.fetchMoreVariable = infiniteConfig.getFetchMore(newData, data)
-            this.state.canFetchMore = this.fetchMoreVariable !== false
+            this.state.canFetchMore = Boolean(this.fetchMoreVariable)
 
             return (data as unknown) as TResult
           } finally {


Test patch:
diff --git a/src/react/tests/useInfiniteQuery.test.tsx b/src/react/tests/useInfiniteQuery.test.tsx
index 43327b2de..a37d7364d 100644
--- a/src/react/tests/useInfiniteQuery.test.tsx
+++ b/src/react/tests/useInfiniteQuery.test.tsx
@@ -202,6 +202,84 @@ describe('useInfiniteQuery', () => {
     })
   })
 
+  it('should compute canFetchMore correctly for falsy getFetchMore return value', async () => {
+    function Page() {
+      const fetchCountRef = React.useRef(0)
+      const {
+        status,
+        data,
+        error,
+        isFetching,
+        isFetchingMore,
+        fetchMore,
+        canFetchMore,
+        refetch,
+      } = useInfiniteQuery<Result, Error, string>(
+        'items',
+        (_key, nextId = 0) => fetchItems(nextId, fetchCountRef.current++),
+        {
+          getFetchMore: (_lastGroup, _allGroups) => undefined,
+        }
+      )
+
+      return (
+        <div>
+          <h1>Pagination</h1>
+          {status === 'loading' ? (
+            'Loading...'
+          ) : status === 'error' ? (
+            <span>Error: {error?.message}</span>
+          ) : (
+            <>
+              <div>Data:</div>
+              {data?.map((page, i) => (
+                <div key={i}>
+                  <div>
+                    Page {i}: {page.ts}
+                  </div>
+                  <div key={i}>
+                    {page.items.map(item => (
+                      <p key={item}>Item: {item}</p>
+                    ))}
+                  </div>
+                </div>
+              ))}
+              <div>
+                <button
+                  onClick={() => fetchMore()}
+                  disabled={!canFetchMore || Boolean(isFetchingMore)}
+                >
+                  {isFetchingMore
+                    ? 'Loading more...'
+                    : canFetchMore
+                    ? 'Load More'
+                    : 'Nothing more to load'}
+                </button>
+                <button onClick={() => refetch()}>Refetch</button>
+              </div>
+              <div>
+                {isFetching && !isFetchingMore
+                  ? 'Background Updating...'
+                  : null}
+              </div>
+            </>
+          )}
+        </div>
+      )
+    }
+
+    const rendered = render(<Page />)
+
+    rendered.getByText('Loading...')
+
+    await waitFor(() => {
+      rendered.getByText('Item: 9')
+      rendered.getByText('Page 0: 0')
+    })
+
+    rendered.getByText('Nothing more to load')
+  })
+
   it('should compute canFetchMore correctly using initialData', async () => {
     function Page() {
       const fetchCountRef = React.useRef(0)
@@ -293,6 +371,81 @@ describe('useInfiniteQuery', () => {
     })
   })
 
+  it('should compute canFetchMore correctly for falsy getFetchMore return value using initialData', async () => {
+    function Page() {
+      const fetchCountRef = React.useRef(0)
+      const {
+        status,
+        data,
+        error,
+        isFetching,
+        isFetchingMore,
+        fetchMore,
+        canFetchMore,
+        refetch,
+      } = useInfiniteQuery<Result, Error, string>(
+        'items',
+        (_key, nextId = 0) => fetchItems(nextId, fetchCountRef.current++),
+        {
+          initialData: [initialItems(0)],
+          getFetchMore: (_lastGroup, _allGroups) => undefined,
+        }
+      )
+
+      return (
+        <div>
+          <h1>Pagination</h1>
+          {status === 'loading' ? (
+            'Loading...'
+          ) : status === 'error' ? (
+            <span>Error: {error?.message}</span>
+          ) : (
+            <>
+              <div>Data:</div>
+              {data?.map((page, i) => (
+                <div key={i}>
+                  <div>
+                    Page {i}: {page.ts}
+                  </div>
+                  <div key={i}>
+                    {page.items.map(item => (
+                      <p key={item}>Item: {item}</p>
+                    ))}
+                  </div>
+                </div>
+              ))}
+              <div>
+                <button
+                  onClick={() => fetchMore()}
+                  disabled={!canFetchMore || Boolean(isFetchingMore)}
+                >
+                  {isFetchingMore
+                    ? 'Loading more...'
+                    : canFetchMore
+                    ? 'Load More'
+                    : 'Nothing more to load'}
+                </button>
+                <button onClick={() => refetch()}>Refetch</button>
+              </div>
+              <div>
+                {isFetching && !isFetchingMore
+                  ? 'Background Updating...'
+                  : null}
+              </div>
+            </>
+          )}
+        </div>
+      )
+    }
+
+    const rendered = render(<Page />)
+
+    rendered.getByText('Item: 9')
+    rendered.getByText('Page 0: 0')
+
+    rendered.getByText('Nothing more to load')
+  })
+
   it('should build fresh cursors on refetch', async () => {
     const genItems = (size: number) =>
       [...new Array(size)].fill(null).map((_, d) => d)