UNPKG

5.2 kBJavaScriptView Raw
1import { useCallback, useContext, useEffect, useReducer, useMemo, useRef } from 'react';
2import PaginationContext from '@jetshop/core/components/Pagination/PaginationContext';
3import { useApolloClient } from 'react-apollo';
4import get from 'lodash.get';
5function getProductsFromPreviousPages(products, client, query, variables) {
6 const nextVariables = Object.assign(Object.assign({}, variables), { offset: variables.offset - variables.first });
7 try {
8 const res = client.readQuery({
9 query,
10 variables: nextVariables
11 });
12 return getProductsFromPreviousPages(res.route.object.products.result.concat(products), client, query, nextVariables);
13 }
14 catch (e) {
15 return { prevOffset: nextVariables.offset, products };
16 }
17}
18function getProductsFromNextPages(products, client, query, variables) {
19 const nextVariables = Object.assign(Object.assign({}, variables), { offset: variables.offset + variables.first });
20 try {
21 const res = client.readQuery({
22 query,
23 variables: nextVariables
24 });
25 return getProductsFromNextPages(products.concat(res.route.object.products.result), client, query, nextVariables);
26 }
27 catch (e) {
28 return { nextOffset: nextVariables.offset, products };
29 }
30}
31function getAdjacentProducts(products, client, query, variables) {
32 const { prevOffset, products: prevProducts } = getProductsFromPreviousPages(products, client, query, variables);
33 const { nextOffset, products: allProducts } = getProductsFromNextPages(prevProducts, client, query, variables);
34 return {
35 prevOffset,
36 nextOffset,
37 products: allProducts
38 };
39}
40const initialState = {
41 previous: false,
42 next: false
43};
44function reducer(state, action) {
45 switch (action.type) {
46 case 'SUCCESS':
47 case 'ERROR':
48 return Object.assign(Object.assign({}, state), { [action.payload]: false });
49 case 'FETCHING':
50 return Object.assign(Object.assign({}, state), { [action.payload]: true });
51 default:
52 return state;
53 }
54}
55const useInfinitePagination = ({ result, query }) => {
56 const category = get(result, 'data.route.object', {});
57 const [loading, dispatch] = useReducer(reducer, initialState);
58 const { perPage, offset, goToPage } = useContext(PaginationContext);
59 const totalResults = get(category, 'products.totalResults', 0);
60 const client = useApolloClient();
61 // When load more is clicked, the new offset for the query is changed
62 const fetchProductsByOffset = useCallback(newOffset => {
63 const payload = newOffset < offset ? 'previous' : 'next';
64 dispatch({
65 type: 'FETCHING',
66 payload
67 });
68 client
69 .query({
70 query,
71 variables: Object.assign(Object.assign({}, result.variables), { offset: newOffset }),
72 errorPolicy: 'all',
73 fetchPolicy: 'network-only'
74 })
75 .then(() => dispatch({
76 type: 'SUCCESS',
77 payload
78 }), () => dispatch({
79 type: 'ERROR',
80 payload
81 }));
82 }, [offset, client, result, query]);
83 // If entered page number is too big, this navigate to the highest page number
84 useEffect(() => {
85 if (totalResults > 0 && totalResults < offset) {
86 const lastPage = Math.ceil(totalResults / perPage);
87 goToPage(lastPage);
88 }
89 }, [totalResults, perPage, offset, goToPage]);
90 const { prevOffset, nextOffset, products } = getAdjacentProducts(get(category, 'products.result', []), client, query, Object.assign(Object.assign({}, result.variables), { offset }));
91 // getAdjacentProducts will always return a new array, since we concatenate multiple queries
92 // To avoid unnecessary re-renders, we memoize this in a ref
93 const prevProducts = useRef([]);
94 const memoizedProducts = useMemo(() => {
95 const productsLength = products.length;
96 if (productsLength === prevProducts.current.length) {
97 let hasMismatch = false;
98 for (let i = 0; i < productsLength; i++) {
99 if (products[i] !== prevProducts.current[i]) {
100 hasMismatch = true;
101 break;
102 }
103 }
104 if (!hasMismatch) {
105 return prevProducts.current;
106 }
107 }
108 prevProducts.current = products;
109 return prevProducts.current;
110 }, [products]);
111 return {
112 previous: {
113 loadingProducts: loading.previous,
114 hasProducts: prevOffset >= 0,
115 fetchProducts: () => fetchProductsByOffset(prevOffset),
116 offset: prevOffset,
117 page: prevOffset / perPage + 1
118 },
119 next: {
120 loadingProducts: loading.next,
121 hasProducts: nextOffset <= totalResults,
122 fetchProducts: () => fetchProductsByOffset(nextOffset),
123 offset: nextOffset,
124 page: nextOffset / perPage
125 },
126 products: memoizedProducts
127 };
128};
129export default useInfinitePagination;
130//# sourceMappingURL=useInfinitePagination.js.map
\No newline at end of file