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 = () => { <ProductsFilter /> </FlexWrapper> - {isLoading ? ( - <Loader /> - ) : products.length !== 0 ? ( + {products.length !== 0 ? ( <Scrollbar width={{ dt: '868' }}> <InfiniteScroll - pageStart={0} + pageStart={1} loadMore={() => { 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 <margogerasimenko@gmail.com> 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; } `;