From 81692ccb89b8d4c74d4d76f6972c6470ece636bc Mon Sep 17 00:00:00 2001
From: P-Burchenkov
Date: Wed, 27 Sep 2023 20:36:25 +0300
Subject: [PATCH 1/4] try-to-do
---
package-lock.json | 12 ++++
package.json | 1 +
.../ProductOrExerciseContainer.jsx | 18 ++++-
.../ProductsFilter/ProductsFilter.jsx | 2 +
src/components/Scrollbar/Scrollbar.syled.jsx | 4 +-
src/pages/Products/Products.jsx | 70 +++++++++++++++----
src/redux/productsFilter/operations.js | 12 ++++
src/redux/productsFilter/selectors.js | 3 +
src/redux/productsFilter/slice.js | 35 ++++++++--
9 files changed, 135 insertions(+), 22 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index f03d7551..5a336f27 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -30,6 +30,7 @@
"react-countdown-circle-timer": "^3.2.1",
"react-datepicker": "^4.18.0",
"react-dom": "^18.2.0",
+ "react-infinite-scroller": "^1.2.6",
"react-loader-spinner": "^5.4.5",
"react-redux": "^8.1.2",
"react-responsive": "^9.0.2",
@@ -7001,6 +7002,17 @@
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
},
+ "node_modules/react-infinite-scroller": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/react-infinite-scroller/-/react-infinite-scroller-1.2.6.tgz",
+ "integrity": "sha512-mGdMyOD00YArJ1S1F3TVU9y4fGSfVVl6p5gh/Vt4u99CJOptfVu/q5V/Wlle72TMgYlBwIhbxK5wF0C/R33PXQ==",
+ "dependencies": {
+ "prop-types": "^15.5.8"
+ },
+ "peerDependencies": {
+ "react": "^0.14.9 || ^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
diff --git a/package.json b/package.json
index 03e6eb05..486976e8 100644
--- a/package.json
+++ b/package.json
@@ -33,6 +33,7 @@
"react-countdown-circle-timer": "^3.2.1",
"react-datepicker": "^4.18.0",
"react-dom": "^18.2.0",
+ "react-infinite-scroller": "^1.2.6",
"react-loader-spinner": "^5.4.5",
"react-redux": "^8.1.2",
"react-responsive": "^9.0.2",
diff --git a/src/components/ProductOrExerciseContainer/ProductOrExerciseContainer.jsx b/src/components/ProductOrExerciseContainer/ProductOrExerciseContainer.jsx
index f6aa0fbb..2b563280 100644
--- a/src/components/ProductOrExerciseContainer/ProductOrExerciseContainer.jsx
+++ b/src/components/ProductOrExerciseContainer/ProductOrExerciseContainer.jsx
@@ -1,5 +1,21 @@
+import { useRef, useEffect } from 'react';
+import { useSelector } from 'react-redux';
+
+import { getSearchParams } from '../../redux/productsFilter/selectors';
import { Container } from './ProductOrExerciseContainer.styled';
export default function ProductsOrExercisesContainer({ children, ...props }) {
- return {children};
+ const containerRef = useRef();
+ const searchParams = useSelector(getSearchParams);
+
+ useEffect(() => {
+ console.log('scroll', containerRef.current.firstChild);
+ containerRef.current.firstChild?.scrollIntoView();
+ }, [searchParams]);
+
+ return (
+
+ {children}
+
+ );
}
diff --git a/src/components/ProductsFilter/ProductsFilter.jsx b/src/components/ProductsFilter/ProductsFilter.jsx
index c1156cb1..505927ae 100644
--- a/src/components/ProductsFilter/ProductsFilter.jsx
+++ b/src/components/ProductsFilter/ProductsFilter.jsx
@@ -23,6 +23,7 @@ import {
import debounce from 'lodash.debounce';
import { capitalizeWord } from '../../utils/capitalizeWord';
+import { addSearchParams } from '../../redux/productsFilter/slice';
export default function ProductsFilter() {
const makeReqObj = (input, category, recommended) => {
@@ -52,6 +53,7 @@ export default function ProductsFilter() {
useEffect(() => {
const reqObj = makeReqObj(query, currentCategory, isRecommended);
const urlParams = new URLSearchParams(reqObj).toString();
+ dispatch(addSearchParams(urlParams));
dispatch(fetchProducts(urlParams));
}, [query, currentCategory, isRecommended, dispatch]);
diff --git a/src/components/Scrollbar/Scrollbar.syled.jsx b/src/components/Scrollbar/Scrollbar.syled.jsx
index fbe700a0..12cad866 100644
--- a/src/components/Scrollbar/Scrollbar.syled.jsx
+++ b/src/components/Scrollbar/Scrollbar.syled.jsx
@@ -3,13 +3,11 @@ import { mq } from '../../utils';
export const ScrollContainer = styled.div`
width: ${({ width }) => width?.mob + 'px' || '100%'};
+ height: 100vh;
${mq.tablet} {
width: ${({ width }) => width?.nab + 'px' || '100%'};
- height: 660px;
}
${mq.desktop} {
width: ${({ width }) => width?.dt + 'px' || '100%'};
- height: 487px;
}
-
`;
diff --git a/src/pages/Products/Products.jsx b/src/pages/Products/Products.jsx
index 20cad700..74cde370 100644
--- a/src/pages/Products/Products.jsx
+++ b/src/pages/Products/Products.jsx
@@ -5,22 +5,42 @@ import { FlexWrapper, ProductPageContainer } from './Products.styled';
import Scrollbar from '../../components/Scrollbar';
import ProductsOrExercisesItem from '../../components/ProductsOrExercisesItem/ProductsOrExercisesItem';
import { useDispatch, useSelector } from 'react-redux';
-import { getProducts } from '../../redux/productsFilter/selectors';
+
import { getAddProductIsLoading } from '../../redux/products/selectors';
-import { useEffect } from 'react';
+import { useEffect, useState } from 'react';
import { fetchProducts } from '../../redux/productsFilter/operations';
import EmptyProductList from '../../components/EmptyProductList/EmptyProductList';
import Loader from '../../components/Lodaer/Loader';
+import { fetchMoreProducts } from '../../redux/productsFilter/operations';
+import {
+ getIsLoading,
+ getSearchParams,
+ getProducts,
+ getHasMore,
+} from '../../redux/productsFilter/selectors';
+import InfiniteScroll from 'react-infinite-scroller';
const Products = () => {
+ const [page, setPage] = useState(1);
+
+ const hasMore = useSelector(getHasMore);
+
+ const isLoadingMoreProducts = useSelector(getIsLoading);
+
const dispatch = useDispatch();
const products = useSelector(getProducts);
const isLoading = useSelector(getAddProductIsLoading);
+ const searchParams = useSelector(getSearchParams);
+
useEffect(() => {
dispatch(fetchProducts());
}, [dispatch]);
+ useEffect(() => {
+ setPage(1);
+ }, [searchParams]);
+
return (
@@ -31,17 +51,43 @@ const Products = () => {
) : products.length !== 0 ? (
-
- {products.map(product => {
- return (
-
+ {
+ if (page === 1) {
+ setPage(prevPage => prevPage + 1);
+ return;
+ }
+ console.log('page');
+ const urlParams = { page, limit: 20 };
+ const paginationParams = new URLSearchParams(
+ urlParams,
+ ).toString();
+ dispatch(
+ fetchMoreProducts(`${searchParams}&${paginationParams}`),
);
- })}
-
+ setPage(prevPage => prevPage + 1);
+ }}
+ hasMore={hasMore && !isLoadingMoreProducts}
+ loader={
+
+ Loading ...
+
+ }
+ useWindow={false}
+ >
+
+ {products.map(product => {
+ return (
+
+ );
+ })}
+
+
) : (
diff --git a/src/redux/productsFilter/operations.js b/src/redux/productsFilter/operations.js
index e1bade3c..7d50cee0 100644
--- a/src/redux/productsFilter/operations.js
+++ b/src/redux/productsFilter/operations.js
@@ -33,3 +33,15 @@ export const getCategories = createAsyncThunk(
},
},
);
+
+export const fetchMoreProducts = createAsyncThunk(
+ `fetchMoreProducts`,
+ async (params, thunkAPI) => {
+ try {
+ const { data } = await axios.get(`api/products?${params}`);
+ return data;
+ } catch (error) {
+ return thunkAPI.rejectWithValue(error.message);
+ }
+ },
+);
diff --git a/src/redux/productsFilter/selectors.js b/src/redux/productsFilter/selectors.js
index 38b81b28..5cc71aa4 100644
--- a/src/redux/productsFilter/selectors.js
+++ b/src/redux/productsFilter/selectors.js
@@ -1,2 +1,5 @@
export const getProducts = state => state.products.products;
export const getProductsCategories = state => state.products.categories;
+export const getIsLoading = state => state.products.isLoading;
+export const getSearchParams = state => state.products.searchParams;
+export const getHasMore = state => state.products.hasMore;
diff --git a/src/redux/productsFilter/slice.js b/src/redux/productsFilter/slice.js
index 43ce8af4..e3ad81f4 100644
--- a/src/redux/productsFilter/slice.js
+++ b/src/redux/productsFilter/slice.js
@@ -1,22 +1,29 @@
import { createSlice } from '@reduxjs/toolkit';
-import { fetchProducts, getCategories } from './operations';
-
-
+import { fetchProducts, getCategories, fetchMoreProducts } from './operations';
export const productsSlice = createSlice({
name: 'products',
- initialState : {
+ initialState: {
products: [],
categories: [],
error: null,
isLoading: false,
+ searchParams: '',
+ hasMore: false,
+ },
+ reducers: {
+ addSearchParams: {
+ reducer(state, action) {
+ state.searchParams = action.payload;
+ },
+ },
},
- reducers: {},
extraReducers: builder => {
builder.addCase(fetchProducts.fulfilled, (state, action) => {
state.products = action.payload;
state.error = null;
state.isLoading = false;
+ state.hasMore = action.payload.length < 20 ? false : true;
});
builder.addCase(fetchProducts.pending, (state, action) => {
state.isLoading = true;
@@ -37,6 +44,22 @@ export const productsSlice = createSlice({
state.error = action.payload;
state.isLoading = false;
});
+ builder.addCase(fetchMoreProducts.fulfilled, (state, action) => {
+ state.products = [...state.products, ...action.payload];
+ state.error = null;
+ state.isLoading = false;
+ state.hasMore = action.payload.length < 20 ? false : true;
+ });
+ builder.addCase(fetchMoreProducts.pending, (state, action) => {
+ state.isLoading = true;
+ });
+ builder.addCase(fetchMoreProducts.rejected, (state, action) => {
+ state.error = action.payload;
+ state.isLoading = false;
+ });
},
});
-export default productsSlice.reducer;
\ No newline at end of file
+
+export default productsSlice.reducer;
+
+export const { addSearchParams } = productsSlice.actions;
From f13506724fb90bd2d9365803bd6563d39940f5ae Mon Sep 17 00:00:00 2001
From: P-Burchenkov
Date: Wed, 27 Sep 2023 20:50:48 +0300
Subject: [PATCH 2/4] work
---
src/pages/Products/Products.jsx | 13 +++----------
1 file changed, 3 insertions(+), 10 deletions(-)
diff --git a/src/pages/Products/Products.jsx b/src/pages/Products/Products.jsx
index 74cde370..465a02f3 100644
--- a/src/pages/Products/Products.jsx
+++ b/src/pages/Products/Products.jsx
@@ -29,14 +29,9 @@ const Products = () => {
const dispatch = useDispatch();
const products = useSelector(getProducts);
- const isLoading = useSelector(getAddProductIsLoading);
const searchParams = useSelector(getSearchParams);
- useEffect(() => {
- dispatch(fetchProducts());
- }, [dispatch]);
-
useEffect(() => {
setPage(1);
}, [searchParams]);
@@ -47,18 +42,16 @@ const Products = () => {
- {isLoading ? (
-
- ) : products.length !== 0 ? (
+ {products.length !== 0 ? (
{
if (page === 1) {
setPage(prevPage => prevPage + 1);
return;
}
- console.log('page');
+
const urlParams = { page, limit: 20 };
const paginationParams = new URLSearchParams(
urlParams,
From 709a36eb98160fb4607e97d25ac0527736747486 Mon Sep 17 00:00:00 2001
From: nzend <115723110+nzend@users.noreply.github.com>
Date: Wed, 27 Sep 2023 21:05:22 +0300
Subject: [PATCH 3/4] Update Scrollbar.syled.jsx
---
src/components/Scrollbar/Scrollbar.syled.jsx | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/components/Scrollbar/Scrollbar.syled.jsx b/src/components/Scrollbar/Scrollbar.syled.jsx
index 12cad866..5e62d0a8 100644
--- a/src/components/Scrollbar/Scrollbar.syled.jsx
+++ b/src/components/Scrollbar/Scrollbar.syled.jsx
@@ -3,11 +3,13 @@ import { mq } from '../../utils';
export const ScrollContainer = styled.div`
width: ${({ width }) => width?.mob + 'px' || '100%'};
- height: 100vh;
+
${mq.tablet} {
width: ${({ width }) => width?.nab + 'px' || '100%'};
+
}
${mq.desktop} {
width: ${({ width }) => width?.dt + 'px' || '100%'};
+ height: calc(100vh - 220px);
}
`;
From 0cef927d47006e128c62747ca5f5d2e099377f6f Mon Sep 17 00:00:00 2001
From: MargoMarm
Date: Wed, 27 Sep 2023 20:38:19 +0200
Subject: [PATCH 4/4] Update Exercises.styled.jsx
---
src/pages/Exercises/Exercises.styled.jsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/pages/Exercises/Exercises.styled.jsx b/src/pages/Exercises/Exercises.styled.jsx
index 4de32c37..e50f1034 100644
--- a/src/pages/Exercises/Exercises.styled.jsx
+++ b/src/pages/Exercises/Exercises.styled.jsx
@@ -32,11 +32,11 @@ export const ExercisesListContainer = styled.div`
${mq.tablet} {
- padding: 72px 32px 48px 32px;
+ padding: 32px 32px 48px 32px;
}
${mq.desktop} {
- padding: 68px 81px 81px 96px;
+ padding: 0px 81px 81px 96px;
}
`;