Pagination
Loadable fields can also be used to paginate.
This documentation demonstrates the useSkipLimitPagination
hook. You can also use useConnectionSpecPagination
if your connection field conforms to the Relay connection spec.
This API is likely to get simplified substantially, as we support @loadable
on linked server fields.
See this file for a real-world example.
Walk through
First, define a client field (without @component
) that returns an array of items and accepts skip
and limit
parameters. It can accept other params:
import { iso } from '@iso';
export const PetCheckinsCardList = iso(`
field Pet.PetCheckinsCardList($skip: Int, $limit: Int) {
checkins(skip: $skip, limit: $limit) {
CheckinDisplay
id
}
}
`)(function PetCheckinsCardComponent({ data }) {
return data.checkins;
});
Next, select that field loadably.
import { iso } from '@iso';
export const PetDetailDeferredRouteComponent = iso(`
field Query.PetCheckinListRoute($id: ID!) @component {
pet(id: $id) {
PetCheckinsCardList @loadable(lazyLoadArtifact: true)
}
}
`)(function PetDetailRouteComponent({ data }) {});
Next, pass that loadable field to useSkipLimitPagination
:
import { iso } from '@iso';
export const PetDetailDeferredRouteComponent = iso(`
field Pet.PetCheckins @component {
PetCheckinsCardList @loadable(lazyLoadArtifact: true)
}
`)(function PetDetailRouteComponent(pet) {
const skipLimitPaginationState = useSkipLimitPagination(
pet.PetCheckinsCardList,
);
return (
<>
<Button
onClick={() =>
skipLimitPaginationState.kind === 'Complete'
? skipLimitPaginationState.fetchMore(undefined, count)
: null
}
disabled={skipLimitPaginationState.kind === 'Pending'}
variant="contained"
>
Load more
</Button>
{skipLimitPaginationState.results.map((item) => (
<div key={item.id}>
<item.CheckinDisplay />
</div>
))}
{skipLimitPaginationState.kind === 'Pending' && <div>Loading...</div>}
</>
);
});
Prefetching initial pages
Each pagination hook accepts a second initialState
parameter. You can fetch data as part of the parent query (or anywhere, in fact!) and pass the appropriate value to that initialState
. Consider:
export const Newsfeed = iso(`
field Query.Newsfeed @component {
viewer {
newsfeed(skip: 0, limit: 6) {
NewsfeedAdOrBlog
}
NewsfeedPaginationComponent @loadable
}
}
`)(function PetDetailRouteComponent({ data }) {
const viewer = data.viewer;
const paginationState = useSkipLimitPagination(
viewer.NewsfeedPaginationComponent,
{ skip: viewer.newsfeed.length },
);
const newsfeedItems = viewer.newsfeed.concat(paginationState.results);
// ...
});
Data-driven dependencies
Check out the data driven dependencies documentation to see how to combine @loadable
fields, pagination and asConcreteType
fields to fetch the minimal amount of data and JavaScript needed!