Created
November 28, 2019 14:30
-
-
Save sibelius/762fb2d808756e21412e5624ec02f21c to your computer and use it in GitHub Desktop.
back and forward pagination
This file contains hidden or 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
import dot from 'dot-object'; | |
import { stringify } from 'query-string'; | |
import React, { useState } from 'react'; | |
import { RelayRefetchProp } from 'react-relay'; | |
import { RouteComponentProps, useHistory } from 'react-router-dom'; | |
export type PaginationProps = { | |
handleRowsPerPageChange: (quantity: number) => void; | |
totalItems: number; | |
rowsPerPage: number; | |
firstItemIndex: number; | |
lastItemIndex: number; | |
handlePageChange: (isForward: boolean) => void; | |
}; | |
type PageInfo = { | |
hasNextPage: boolean; | |
startCursor: string | null; | |
endCursor: string | null; | |
}; | |
type Edge<T> = { | |
cursor: string; | |
node: T; | |
}; | |
type Connection<T> = { | |
count: number; | |
totalCount: number; | |
endCursorOffset: number; | |
startCursorOffset: number; | |
pageInfo: PageInfo; | |
edges: Edge<T>[]; | |
}; | |
type Props<T> = { | |
children: (edges: Edge<T>[], paginationProps?: PaginationProps) => React.ReactNode; | |
connection: Connection<T>; | |
relay: RelayRefetchProp; | |
getFragmentVariables?: () => object; | |
getRefetchRenderVariables?: () => object; | |
} & RouteComponentProps; | |
export const useRelayPagination = <T extends object>(props: Props<T>) => { | |
const { connection, relay, getFragmentVariables, getRefetchRenderVariables } = props; | |
const history = useHistory(); | |
const [isLoading, setIsLoading] = useState<boolean>(false); | |
const [quantityPerPage, setQuantityPerPage] = useState<number>( | |
getFiltersFromLocation(['quantity']).quantity || props.quantityPerPage || 10, | |
); | |
const safeGetFragmentVariables = () => { | |
if (getFragmentVariables) { | |
return getFragmentVariables(); | |
} | |
return {}; | |
}; | |
const getRenderVariables = () => { | |
if (getRefetchRenderVariables) { | |
return getRefetchRenderVariables(); | |
} | |
return {}; | |
}; | |
const loadPageForwardVars = (quantityPerPageChange: number) => { | |
const { quantity } = getQuery(); | |
const { endCursorOffset, startCursorOffset } = connection; | |
const items = connection.edges.slice(startCursorOffset, endCursorOffset); | |
const lastItem = items[items.length - 1]; | |
const refetchVariables = (fragmentVariables: any) => ({ | |
...fragmentVariables, | |
...safeGetFragmentVariables(), | |
first: parseInt(quantity || quantityPerPage, 10) || 10, | |
after: quantityPerPageChange ? null : lastItem.cursor, | |
}); | |
return refetchVariables; | |
}; | |
const loadPageBackwardsVars = () => { | |
const { quantity } = getQuery(); | |
const { endCursorOffset, startCursorOffset } = connection; | |
const firstItem = connection.edges.slice(startCursorOffset, endCursorOffset)[0].cursor; | |
const refetchVariables = (fragmentVariables: any) => ({ | |
...fragmentVariables, | |
...safeGetFragmentVariables(), | |
last: parseInt(quantity || quantityPerPage, 10) || 10, | |
first: null, | |
before: firstItem, | |
}); | |
return refetchVariables; | |
}; | |
const loadPage = (isForward: boolean, quantity?: number) => { | |
setIsLoading(true); | |
const refetchVariables = isForward ? loadPageForwardVars(quantity) : loadPageBackwardsVars(); | |
const renderVariables = getRenderVariables(); | |
relay.refetch(refetchVariables, renderVariables, () => { | |
setIsLoading(false); | |
}); | |
}; | |
const handleRowsPerPageChange = (quantity: number) => { | |
const { pathname } = location; | |
const queryParams = getQuery(); | |
const search = stringify(dot.dot({ ...queryParams, quantity })); | |
history.replace({ pathname, search }); | |
if (setQuantityPerPage) { | |
setQuantityPerPage(quantity); | |
} | |
loadPage(true, quantity); | |
}; | |
const handleLoading = (value: boolean, callback: () => void) => { | |
setIsLoading(value); | |
callback && callback(); | |
}; | |
const { count, totalCount, endCursorOffset, startCursorOffset } = connection; | |
const slicedEdges = connection.edges.slice(startCursorOffset, endCursorOffset); | |
const paginationProps = { | |
handleRowsPerPageChange, | |
rowsPerPage: quantityPerPage, | |
totalItems: count || totalCount, | |
firstItemIndex: startCursorOffset, | |
lastItemIndex: endCursorOffset, | |
handlePageChange: loadPage, | |
isLoading, | |
}; | |
return { | |
edges: slicedEdges, | |
paginationProps, | |
handleLoading, | |
isLoading, | |
setIsLoading, | |
quantityPerPage, | |
setQuantityPerPage, | |
}; | |
}; | |
export default useRelayPagination; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment