Skip to content

Commit 71e8e4d

Browse files
author
Sanjeev Yadav
committed
refactor: Remove local pagination logic in useCategoryProducts hook
1 parent 9ab1b04 commit 71e8e4d

File tree

8 files changed

+75
-82
lines changed

8 files changed

+75
-82
lines changed

src/apollo/client.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ export async function getApolloClient(): Promise<ApolloClient<any>> {
1717
Query: {
1818
fields: {
1919
products: {
20-
// Don't cache separate results based on
20+
// Cache separate results based on
2121
// any of this field's arguments.
22-
keyArgs: ['search'],
22+
keyArgs: ['search', 'filter'],
2323
// Concatenate the incoming list items with
2424
// the existing list items.
25-
merge(existing, incoming, { args: { search, currentPage } }) {
26-
if (currentPage === 1 || !search) {
25+
merge(existing, incoming, { args: { currentPage } }) {
26+
if (currentPage === 1) {
2727
return incoming;
2828
}
2929
const _existing = existing ?? { items: [] };

src/constants/limits.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const LIMITS = Object.freeze({
2+
categoryProductsPageSize: 10,
23
searchTextMinLength: 3,
34
searchScreenPageSize: 10,
45
autoSearchApiTimeDelay: 1000,

src/logic/products/useCategoryProducts.ts

Lines changed: 21 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,72 @@
11
import { useEffect, useState } from 'react';
2-
import { useLazyQuery, ApolloError } from '@apollo/client';
2+
import { useQuery, ApolloError } from '@apollo/client';
33
import {
44
GET_CATGEORY_PRODUCTS,
55
GetCategoryProductsVars,
66
CategoryProductsDataType,
77
} from '../../apollo/queries/getCategoryProducts';
8-
import { ProductInListType } from '../../apollo/queries/productsFragment';
8+
import { LIMITS } from '../../constants';
99

1010
interface Props {
1111
categoryId: string;
1212
}
1313

1414
interface Result {
15-
products: Array<ProductInListType>;
16-
getCategoryProducts(): void;
15+
data: CategoryProductsDataType | undefined;
1716
loading: boolean;
1817
error: ApolloError | undefined;
1918
currentPage: number;
2019
refresh(): void;
2120
loadMore(): void;
2221
}
2322

24-
const PAGE_SIZE = 10;
25-
2623
export const useCategoryProducts = ({ categoryId: id }: Props): Result => {
27-
const [products, setProducts] = useState<Array<ProductInListType>>([]);
2824
const [currentPage, setCurrentPage] = useState<number>(1);
2925

30-
const [getCategoryProducts, { loading, error }] = useLazyQuery<
26+
const { refetch, data, loading, error, fetchMore } = useQuery<
3127
CategoryProductsDataType,
3228
GetCategoryProductsVars
3329
>(GET_CATGEORY_PRODUCTS, {
3430
variables: {
3531
id,
36-
pageSize: PAGE_SIZE,
37-
currentPage,
38-
},
39-
fetchPolicy: 'no-cache',
40-
onCompleted: responseData => {
41-
if (responseData?.products?.items && currentPage === 1) {
42-
setProducts(responseData.products.items);
43-
} else if (
44-
responseData?.products?.items &&
45-
products.length < responseData.products.total_count &&
46-
products.length < currentPage * PAGE_SIZE
47-
) {
48-
setProducts(prevState => [
49-
...prevState,
50-
...responseData?.products?.items,
51-
]);
52-
}
32+
pageSize: LIMITS.categoryProductsPageSize,
33+
currentPage: 1,
5334
},
5435
});
5536

5637
useEffect(() => {
57-
if (!loading) {
58-
getCategoryProducts();
38+
if (!loading && currentPage !== 1) {
39+
fetchMore({
40+
variables: {
41+
currentPage,
42+
},
43+
});
5944
}
60-
}, [currentPage, getCategoryProducts]);
45+
}, [currentPage]);
6146

6247
const loadMore = () => {
6348
if (loading) {
6449
return;
6550
}
6651

67-
if (currentPage * PAGE_SIZE === products.length) {
52+
if (
53+
currentPage * LIMITS.categoryProductsPageSize ===
54+
data?.products?.items?.length &&
55+
data?.products?.items.length < data?.products?.total_count
56+
) {
6857
setCurrentPage(prevState => prevState + 1);
6958
}
7059
};
7160

7261
const refresh = () => {
73-
if (currentPage !== 1) {
74-
setCurrentPage(1);
75-
} else {
76-
getCategoryProducts();
77-
}
62+
refetch();
63+
setCurrentPage(1);
7864
};
7965

8066
return {
81-
products,
67+
data,
8268
loading,
8369
error,
84-
getCategoryProducts,
8570
currentPage,
8671
refresh,
8772
loadMore,

src/logic/products/useSearch.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const useSearch = (): Result => {
2222
const [currentPage, setCurrentPage] = useState<number>(1);
2323
const [
2424
getSearchProducts,
25-
{ called, loading, error, fetchMore, refetch, data },
25+
{ called, loading, error, fetchMore, data },
2626
] = useLazyQuery<SearchProductsDataType, GetSearchProductsVars>(
2727
GET_SEARCH_PRODUCTS,
2828
);
@@ -63,7 +63,11 @@ export const useSearch = (): Result => {
6363
return;
6464
}
6565

66-
if (data && data?.products?.items?.length < data?.products?.total_count) {
66+
if (
67+
currentPage * LIMITS.searchScreenPageSize ===
68+
data?.products?.items?.length &&
69+
data?.products?.items.length < data?.products?.total_count
70+
) {
6771
setCurrentPage(prevPage => prevPage + 1);
6872
}
6973
};

src/screens/HomeScreen/FeaturedProductList.tsx

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
import React from 'react';
12
import { useNavigation } from '@react-navigation/native';
2-
import React, { useEffect } from 'react';
33
import { ActivityIndicator, View, FlatList, StyleSheet } from 'react-native';
44
import { Text } from 'react-native-elements';
55
import { ProductInListType } from '../../apollo/queries/productsFragment';
@@ -17,23 +17,16 @@ const FeaturedProductList = ({
1717
name,
1818
categoryId,
1919
}: Props): React.ReactElement => {
20-
const {
21-
getCategoryProducts,
22-
loading,
23-
products,
24-
error,
25-
} = useCategoryProducts({ categoryId });
20+
const { data, loading, error } = useCategoryProducts({ categoryId });
2621
const navigation = useNavigation();
2722

28-
useEffect(() => {
29-
getCategoryProducts();
30-
}, []);
31-
3223
const onProductItemClicked = (index: number) => {
33-
navigation.navigate(Routes.NAVIGATION_TO_PRODUCT_DETAILS_SCREEN, {
34-
name: products[index].name,
35-
sku: products[index].sku,
36-
});
24+
if (data?.products?.items) {
25+
navigation.navigate(Routes.NAVIGATION_TO_PRODUCT_DETAILS_SCREEN, {
26+
name: data.products.items[index].name,
27+
sku: data.products.items[index].sku,
28+
});
29+
}
3730
};
3831

3932
const renderItem = ({
@@ -75,7 +68,7 @@ const FeaturedProductList = ({
7568
<FlatList
7669
horizontal
7770
showsHorizontalScrollIndicator={false}
78-
data={products}
71+
data={data?.products?.items ?? []}
7972
renderItem={renderItem}
8073
keyExtractor={item => `productListItem${item.sku}`}
8174
/>

src/screens/HomeScreen/HomeScreen.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const HomeScreen = ({}: Props): React.ReactElement => {
2525
/>
2626
{magentoConfig.homeFeaturedCategories.map(featuredCategory => (
2727
<FeaturedProductList
28+
key={String(featuredCategory.id)}
2829
name={featuredCategory.name}
2930
categoryId={featuredCategory.id}
3031
/>

src/screens/ProductListScreen/ProductListScreen.tsx

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect } from 'react';
1+
import React from 'react';
22
import {
33
View,
44
RefreshControl,
@@ -29,8 +29,7 @@ const ProductListScreen = ({
2929
},
3030
}: Props): React.ReactElement => {
3131
const {
32-
getCategoryProducts,
33-
products,
32+
data,
3433
loading,
3534
currentPage,
3635
error,
@@ -40,15 +39,13 @@ const ProductListScreen = ({
4039
categoryId,
4140
});
4241

43-
useEffect(() => {
44-
getCategoryProducts();
45-
}, []);
46-
4742
const onProductItemClicked = (index: number) => {
48-
navigation.navigate(Routes.NAVIGATION_TO_PRODUCT_DETAILS_SCREEN, {
49-
name: products[index].name,
50-
sku: products[index].sku,
51-
});
43+
if (data?.products?.items) {
44+
navigation.navigate(Routes.NAVIGATION_TO_PRODUCT_DETAILS_SCREEN, {
45+
name: data.products.items[index].name,
46+
sku: data.products.items[index].sku,
47+
});
48+
}
5249
};
5350

5451
const renderItem = ({
@@ -67,22 +64,18 @@ const ProductListScreen = ({
6764
);
6865
};
6966

70-
const renderFooter = () => {
71-
if (loading && products.length !== 0) {
72-
return (
73-
<View style={styles.footerContainer}>
74-
<ActivityIndicator size="large" />
75-
</View>
76-
);
77-
}
78-
return null;
79-
};
67+
const renderFooterComponent = () =>
68+
(loading && data?.products?.items?.length !== 0 && (
69+
<View style={styles.footerContainer}>
70+
<ActivityIndicator size="large" />
71+
</View>
72+
)) || <></>;
8073

8174
return (
8275
<GenericTemplate errorMessage={error?.message}>
8376
<FlatList
8477
numColumns={2}
85-
data={products}
78+
data={data?.products?.items ?? []}
8679
renderItem={renderItem}
8780
keyExtractor={item => `productListItem${item.sku}`}
8881
refreshControl={
@@ -92,7 +85,7 @@ const ProductListScreen = ({
9285
/>
9386
}
9487
onEndReached={loadMore}
95-
ListFooterComponent={renderFooter}
88+
ListFooterComponent={renderFooterComponent}
9689
/>
9790
</GenericTemplate>
9891
);

src/screens/SearchScreen/SearchScreen.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ type Props = {
1616
>;
1717
};
1818

19-
// TODO: Implement pagination
2019
const SearchScreen = ({ navigation }: Props): React.ReactElement => {
2120
const {
2221
searchText,
@@ -66,6 +65,13 @@ const SearchScreen = ({ navigation }: Props): React.ReactElement => {
6665
</View>
6766
)) || <></>;
6867

68+
const renderFooterComponent = () =>
69+
(loading && products.length !== 0 && (
70+
<View style={styles.footerContainer}>
71+
<ActivityIndicator size="large" />
72+
</View>
73+
)) || <></>;
74+
6975
return (
7076
<GenericTemplate>
7177
<SearchBar
@@ -77,6 +83,7 @@ const SearchScreen = ({ navigation }: Props): React.ReactElement => {
7783
name: 'arrow-back',
7884
onPress: handleBackPress,
7985
}}
86+
loadingProps={styles.searchBarLoading}
8087
containerStyle={styles.searchBarContainer}
8188
inputContainerStyle={styles.searchBarInputContainer}
8289
/>
@@ -86,12 +93,14 @@ const SearchScreen = ({ navigation }: Props): React.ReactElement => {
8693
renderItem={renderItem}
8794
keyExtractor={item => `productListItem${item.sku}`}
8895
ListEmptyComponent={renderEmptyComponent}
96+
ListFooterComponent={renderFooterComponent}
8997
onEndReached={loadMore}
9098
/>
9199
</GenericTemplate>
92100
);
93101
};
94102

103+
// TODO: Remove hard-coded values
95104
const styles = StyleSheet.create({
96105
center: {
97106
flex: 1,
@@ -119,6 +128,13 @@ const styles = StyleSheet.create({
119128
borderRadius: 0,
120129
backgroundColor: 'white',
121130
},
131+
searchBarLoading: {
132+
color: 'black',
133+
},
134+
footerContainer: {
135+
alignItems: 'center',
136+
marginVertical: SPACING.small,
137+
},
122138
});
123139

124140
export default SearchScreen;

0 commit comments

Comments
 (0)