-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Add example using page and size - add an intersection observer callback ref hook
- Loading branch information
1 parent
337464d
commit e1b53cf
Showing
8 changed files
with
507 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
examples/query/react/infinite-queries/src/app/useIntersectionCallback.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { useCallback, useRef } from "react" | ||
|
||
export function useIntersectionCallback(onIntersectCallback: () => void) { | ||
const intersectionObserverRef = useRef<IntersectionObserver | null>(null) | ||
|
||
return useCallback( | ||
(node: HTMLDivElement | null) => { | ||
if (intersectionObserverRef.current) { | ||
intersectionObserverRef.current.disconnect() | ||
} | ||
|
||
intersectionObserverRef.current = new IntersectionObserver(entries => { | ||
if (entries[0].isIntersecting) { | ||
onIntersectCallback() | ||
} | ||
}) | ||
|
||
if (node) intersectionObserverRef.current.observe(node) | ||
}, | ||
[onIntersectCallback], | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
129 changes: 129 additions & 0 deletions
129
examples/query/react/infinite-queries/src/features/limit-offset/LimitOffsetExample.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
import { createSelector } from "@reduxjs/toolkit" | ||
import { | ||
BaseQueryFn, | ||
TypedUseQueryStateResult, | ||
} from "@reduxjs/toolkit/query/react" | ||
import { Link, useLocation } from "react-router" | ||
import { useIntersectionCallback } from "../../app/useIntersectionCallback" | ||
import { apiWithInfiniteScroll, ProjectsResponse } from "./infiniteScrollApi" | ||
|
||
type ProjectsInfiniteQueryResult = TypedUseQueryStateResult< | ||
{ pages: ProjectsResponse[] }, | ||
unknown, | ||
BaseQueryFn | ||
> | ||
|
||
const selectCombinedProjects = createSelector( | ||
(res: ProjectsInfiniteQueryResult) => { | ||
return res.data | ||
}, | ||
data => data?.pages?.map(item => item?.projects)?.flat(), | ||
) | ||
|
||
function LimitOffsetExample() { | ||
const { | ||
combinedData, | ||
hasPreviousPage, | ||
hasNextPage, | ||
// data, | ||
error, | ||
isFetching, | ||
isLoading, | ||
isError, | ||
fetchNextPage, | ||
fetchPreviousPage, | ||
isFetchingNextPage, | ||
isFetchingPreviousPage, | ||
} = apiWithInfiniteScroll.endpoints.projectsLimitOffset.useInfiniteQuery( | ||
undefined, | ||
{ | ||
selectFromResult: result => { | ||
return { | ||
...result, | ||
combinedData: selectCombinedProjects(result), | ||
} | ||
}, | ||
}, | ||
) | ||
|
||
const intersectionCallbackRef = useIntersectionCallback(fetchNextPage) | ||
const location = useLocation() | ||
|
||
return ( | ||
<div> | ||
<h2>Limit and Offset Infinite Scroll</h2> | ||
{isLoading ? ( | ||
<p>Loading...</p> | ||
) : isError ? ( | ||
<span>Error: {error.message}</span> | ||
) : null} | ||
|
||
<> | ||
<div> | ||
<button | ||
onClick={() => fetchPreviousPage()} | ||
disabled={!hasPreviousPage || isFetchingPreviousPage} | ||
> | ||
{isFetchingPreviousPage | ||
? "Loading more..." | ||
: hasPreviousPage | ||
? "Load Older" | ||
: "Nothing more to load"} | ||
</button> | ||
</div> | ||
<div | ||
style={{ | ||
overflow: "auto", | ||
margin: "1rem 0px", | ||
height: "400px", | ||
}} | ||
> | ||
{combinedData?.map((project, index, arr) => { | ||
return ( | ||
<div | ||
style={{ | ||
margin: "1em 0px", | ||
border: "1px solid gray", | ||
borderRadius: "5px", | ||
padding: "2rem 1rem", | ||
background: `hsla(${project.id * 30}, 60%, 80%, 1)`, | ||
}} | ||
key={project.id} | ||
> | ||
<div> | ||
<div>{`Project ${project.id} (created at: ${project.createdAt})`}</div> | ||
</div> | ||
</div> | ||
) | ||
})} | ||
|
||
<div ref={intersectionCallbackRef} /> | ||
</div> | ||
<div> | ||
<button | ||
onClick={() => fetchNextPage()} | ||
disabled={!hasNextPage || isFetchingNextPage} | ||
> | ||
{isFetchingNextPage | ||
? "Loading more..." | ||
: hasNextPage | ||
? "Load Newer" | ||
: "Nothing more to load"} | ||
</button> | ||
</div> | ||
<div> | ||
{isFetching && !isFetchingPreviousPage && !isFetchingNextPage | ||
? "Background Updating..." | ||
: null} | ||
</div> | ||
</> | ||
|
||
<hr /> | ||
<Link to="/infinite-scroll/about" state={{ from: location.pathname }}> | ||
Go to another page | ||
</Link> | ||
</div> | ||
) | ||
} | ||
|
||
export default LimitOffsetExample |
72 changes: 72 additions & 0 deletions
72
examples/query/react/infinite-queries/src/features/limit-offset/infiniteScrollApi.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { baseApi } from "../baseApi" | ||
|
||
type Project = { | ||
id: number | ||
createdAt: string | ||
} | ||
|
||
export type ProjectsResponse = { | ||
projects: Project[] | ||
numFound: number | ||
serverTime: string | ||
} | ||
|
||
interface ProjectsInitialPageParam { | ||
offset: number | ||
limit: number | ||
} | ||
|
||
export const apiWithInfiniteScroll = baseApi.injectEndpoints({ | ||
endpoints: builder => ({ | ||
projectsLimitOffset: builder.infiniteQuery< | ||
ProjectsResponse, | ||
void, | ||
ProjectsInitialPageParam | ||
>({ | ||
infiniteQueryOptions: { | ||
initialPageParam: { | ||
offset: 0, | ||
limit: 20, | ||
}, | ||
getNextPageParam: ( | ||
lastPage, | ||
allPages, | ||
lastPageParam, | ||
allPageParams, | ||
) => { | ||
const nextOffset = lastPageParam.offset + lastPageParam.limit | ||
const remainingItems = lastPage?.numFound - nextOffset | ||
|
||
if (remainingItems <= 0) { | ||
return undefined | ||
} | ||
|
||
return { | ||
...lastPageParam, | ||
offset: nextOffset, | ||
} | ||
}, | ||
getPreviousPageParam: ( | ||
firstPage, | ||
allPages, | ||
firstPageParam, | ||
allPageParams, | ||
) => { | ||
const prevOffset = firstPageParam.offset - firstPageParam.limit | ||
if (prevOffset < 0) return undefined | ||
|
||
return { | ||
...firstPageParam, | ||
offset: firstPageParam.offset - firstPageParam.limit, | ||
} | ||
}, | ||
}, | ||
query: ({ offset, limit }) => { | ||
return { | ||
url: `https://example.com/api/projectsLimitOffset?offset=${offset}&limit=${limit}`, | ||
method: "GET", | ||
} | ||
}, | ||
}), | ||
}), | ||
}) |
Oops, something went wrong.