Skip to content

Commit 7158e3d

Browse files
authored
fix(useInfiniteQuery): correctly set canFetchMore (TanStack#822)
canFetchMore should be false for falsy getFetchMore return value
1 parent 21d942b commit 7158e3d

File tree

2 files changed

+157
-4
lines changed

2 files changed

+157
-4
lines changed

src/core/query.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,10 @@ export class Query<TResult, TError> {
154154
infiniteData[infiniteData.length - 1],
155155
infiniteData
156156
)
157-
this.state.canFetchMore = this.fetchMoreVariable !== false
157+
this.state.canFetchMore = Boolean(this.fetchMoreVariable)
158158
}
159159

160-
// Here we seed the pageVariabes for the query
160+
// Here we seed the pageVariables for the query
161161
if (!this.pageVariables) {
162162
this.pageVariables = [[...this.queryKey]]
163163
}
@@ -460,7 +460,7 @@ export class Query<TResult, TError> {
460460
data[data.length - 1],
461461
data
462462
)
463-
this.state.canFetchMore = this.fetchMoreVariable !== false
463+
this.state.canFetchMore = Boolean(this.fetchMoreVariable)
464464
this.pageVariables = rebuiltPageVariables
465465

466466
return (data as unknown) as TResult
@@ -497,7 +497,7 @@ export class Query<TResult, TError> {
497497
}
498498

499499
this.fetchMoreVariable = infiniteConfig.getFetchMore(newData, data)
500-
this.state.canFetchMore = this.fetchMoreVariable !== false
500+
this.state.canFetchMore = Boolean(this.fetchMoreVariable)
501501

502502
return (data as unknown) as TResult
503503
} finally {

src/react/tests/useInfiniteQuery.test.tsx

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,84 @@ describe('useInfiniteQuery', () => {
202202
})
203203
})
204204

205+
it('should compute canFetchMore correctly for falsy getFetchMore return value', async () => {
206+
function Page() {
207+
const fetchCountRef = React.useRef(0)
208+
const {
209+
status,
210+
data,
211+
error,
212+
isFetching,
213+
isFetchingMore,
214+
fetchMore,
215+
canFetchMore,
216+
refetch,
217+
} = useInfiniteQuery<Result, Error, string>(
218+
'items',
219+
(_key, nextId = 0) => fetchItems(nextId, fetchCountRef.current++),
220+
{
221+
getFetchMore: (_lastGroup, _allGroups) => undefined,
222+
}
223+
)
224+
225+
return (
226+
<div>
227+
<h1>Pagination</h1>
228+
{status === 'loading' ? (
229+
'Loading...'
230+
) : status === 'error' ? (
231+
<span>Error: {error?.message}</span>
232+
) : (
233+
<>
234+
<div>Data:</div>
235+
{data?.map((page, i) => (
236+
<div key={i}>
237+
<div>
238+
Page {i}: {page.ts}
239+
</div>
240+
<div key={i}>
241+
{page.items.map(item => (
242+
<p key={item}>Item: {item}</p>
243+
))}
244+
</div>
245+
</div>
246+
))}
247+
<div>
248+
<button
249+
onClick={() => fetchMore()}
250+
disabled={!canFetchMore || Boolean(isFetchingMore)}
251+
>
252+
{isFetchingMore
253+
? 'Loading more...'
254+
: canFetchMore
255+
? 'Load More'
256+
: 'Nothing more to load'}
257+
</button>
258+
<button onClick={() => refetch()}>Refetch</button>
259+
</div>
260+
<div>
261+
{isFetching && !isFetchingMore
262+
? 'Background Updating...'
263+
: null}
264+
</div>
265+
</>
266+
)}
267+
</div>
268+
)
269+
}
270+
271+
const rendered = render(<Page />)
272+
273+
rendered.getByText('Loading...')
274+
275+
await waitFor(() => {
276+
rendered.getByText('Item: 9')
277+
rendered.getByText('Page 0: 0')
278+
})
279+
280+
rendered.getByText('Nothing more to load')
281+
})
282+
205283
it('should compute canFetchMore correctly using initialData', async () => {
206284
function Page() {
207285
const fetchCountRef = React.useRef(0)
@@ -293,6 +371,81 @@ describe('useInfiniteQuery', () => {
293371
})
294372
})
295373

374+
it('should compute canFetchMore correctly for falsy getFetchMore return value using initialData', async () => {
375+
function Page() {
376+
const fetchCountRef = React.useRef(0)
377+
const {
378+
status,
379+
data,
380+
error,
381+
isFetching,
382+
isFetchingMore,
383+
fetchMore,
384+
canFetchMore,
385+
refetch,
386+
} = useInfiniteQuery<Result, Error, string>(
387+
'items',
388+
(_key, nextId = 0) => fetchItems(nextId, fetchCountRef.current++),
389+
{
390+
initialData: [initialItems(0)],
391+
getFetchMore: (_lastGroup, _allGroups) => undefined,
392+
}
393+
)
394+
395+
return (
396+
<div>
397+
<h1>Pagination</h1>
398+
{status === 'loading' ? (
399+
'Loading...'
400+
) : status === 'error' ? (
401+
<span>Error: {error?.message}</span>
402+
) : (
403+
<>
404+
<div>Data:</div>
405+
{data?.map((page, i) => (
406+
<div key={i}>
407+
<div>
408+
Page {i}: {page.ts}
409+
</div>
410+
<div key={i}>
411+
{page.items.map(item => (
412+
<p key={item}>Item: {item}</p>
413+
))}
414+
</div>
415+
</div>
416+
))}
417+
<div>
418+
<button
419+
onClick={() => fetchMore()}
420+
disabled={!canFetchMore || Boolean(isFetchingMore)}
421+
>
422+
{isFetchingMore
423+
? 'Loading more...'
424+
: canFetchMore
425+
? 'Load More'
426+
: 'Nothing more to load'}
427+
</button>
428+
<button onClick={() => refetch()}>Refetch</button>
429+
</div>
430+
<div>
431+
{isFetching && !isFetchingMore
432+
? 'Background Updating...'
433+
: null}
434+
</div>
435+
</>
436+
)}
437+
</div>
438+
)
439+
}
440+
441+
const rendered = render(<Page />)
442+
443+
rendered.getByText('Item: 9')
444+
rendered.getByText('Page 0: 0')
445+
446+
rendered.getByText('Nothing more to load')
447+
})
448+
296449
it('should build fresh cursors on refetch', async () => {
297450
const genItems = (size: number) =>
298451
[...new Array(size)].fill(null).map((_, d) => d)

0 commit comments

Comments
 (0)