From 717ce5d3320c2d94cc18cb9702e1167a036b34e5 Mon Sep 17 00:00:00 2001 From: Amir Amiri Date: Sun, 5 Mar 2023 16:17:40 +0330 Subject: [PATCH] redux thunk added --- package.json | 2 ++ src/components/spinner/spinner.component.jsx | 8 +++++ src/components/spinner/spinner.styles.jsx | 30 +++++++++++++++++ src/index.js | 11 ++++--- .../categories-preview.component.jsx | 10 ++++-- src/routes/category/category-component.jsx | 17 ++++++---- src/routes/shop/shop.component.jsx | 6 ++-- src/store/category/category.action.js | 18 ++++++++++- src/store/category/category.reducer.js | 12 +++++-- src/store/category/category.selector.js | 5 +++ src/store/category/category.types.js | 5 ++- src/store/store.js | 32 ++++++++----------- yarn.lock | 10 ++++++ 13 files changed, 126 insertions(+), 40 deletions(-) create mode 100644 src/components/spinner/spinner.component.jsx create mode 100644 src/components/spinner/spinner.styles.jsx diff --git a/package.json b/package.json index bafcd68..29d7313 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,8 @@ "react-scripts": "5.0.1", "redux": "^4.2.1", "redux-logger": "^3.0.6", + "redux-persist": "^6.0.0", + "redux-thunk": "^2.4.2", "reselect": "^4.1.7", "sass": "^1.55.0", "styled-components": "^5.3.6", diff --git a/src/components/spinner/spinner.component.jsx b/src/components/spinner/spinner.component.jsx new file mode 100644 index 0000000..8557a97 --- /dev/null +++ b/src/components/spinner/spinner.component.jsx @@ -0,0 +1,8 @@ +import { SpinnerContainer, SpinnerOverlay } from "./spinner.styles"; + +const Spinner = () => + + + + +export default Spinner; \ No newline at end of file diff --git a/src/components/spinner/spinner.styles.jsx b/src/components/spinner/spinner.styles.jsx new file mode 100644 index 0000000..7650f84 --- /dev/null +++ b/src/components/spinner/spinner.styles.jsx @@ -0,0 +1,30 @@ +import styled from "styled-components"; + +export const SpinnerOverlay = styled.div` + height: 60vh; + width: 100%; + display: flex; + justify-content: center; + align-items: center; +`; + +export const SpinnerContainer = styled.div` + display: inline-block; + width: 50px; + height: 50px; + border: 3px solid rgba(195, 195, 195, 0.6); + border-radius: 50%; + border-top-color: #636767; + animation: spin 1s ease-in-out infinite; + -webkit-animation: spin 1s ease-in-out infinite; + @keyframes spin { + to { + -webkit-transform: rotate(360deg); + } + } + @-webkit-keyframes spin { + to { + -webkit-transform: rotate(360deg); + } + } +`; diff --git a/src/index.js b/src/index.js index a371921..2540555 100644 --- a/src/index.js +++ b/src/index.js @@ -5,15 +5,18 @@ import { BrowserRouter } from 'react-router-dom'; import { Provider } from 'react-redux'; import './index.css'; import App from './App'; -import { store } from './store/store'; +import { store, persistor } from './store/store'; +import { PersistGate } from 'redux-persist/integration/react'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( - - - + + + + + ); diff --git a/src/routes/categories-preview/categories-preview.component.jsx b/src/routes/categories-preview/categories-preview.component.jsx index 41191d7..fba9f12 100644 --- a/src/routes/categories-preview/categories-preview.component.jsx +++ b/src/routes/categories-preview/categories-preview.component.jsx @@ -1,18 +1,22 @@ import { Fragment } from "react"; import { useSelector } from "react-redux"; -import { categoriesMapSelector } from "../../store/category/category.selector"; +import { categoriesMapSelector, selectCategoriesIsLoading } from "../../store/category/category.selector"; import CategoryPreview from "../../components/category-preview/category-preview.component"; +import Spinner from "../../components/spinner/spinner.component"; const CategoriesPreview = () => { const categoriesMap = useSelector(categoriesMapSelector); + const isLoading = useSelector(selectCategoriesIsLoading); return ( + { - Object.keys(categoriesMap).map(title => { + isLoading ? () : + (Object.keys(categoriesMap).map(title => { const products = categoriesMap[title]; return - }) + })) } diff --git a/src/routes/category/category-component.jsx b/src/routes/category/category-component.jsx index b63b91d..5f3b261 100644 --- a/src/routes/category/category-component.jsx +++ b/src/routes/category/category-component.jsx @@ -1,12 +1,14 @@ import { Fragment, useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; import { useSelector } from "react-redux"; -import { categoriesMapSelector } from "../../store/category/category.selector"; +import { categoriesMapSelector, selectCategoriesIsLoading } from "../../store/category/category.selector"; import ProductCard from '../../components/product-card/product-card.component'; import './category.styles.scss'; +import Spinner from '../../components/spinner/spinner.component'; const Category = () => { const { category } = useParams() + const isLoading = useSelector(selectCategoriesIsLoading); const categoriesMap = useSelector(categoriesMapSelector); const [products, setProducts] = useState(categoriesMap[category]) useEffect(() => { @@ -18,11 +20,14 @@ const Category = () => {

{category}

-
- {products && - products.map((product) => ) - } -
+ { + isLoading ? () : (
+ {products && + products.map((product) => ) + } +
) + } + ) diff --git a/src/routes/shop/shop.component.jsx b/src/routes/shop/shop.component.jsx index dbcb2ed..c67483c 100644 --- a/src/routes/shop/shop.component.jsx +++ b/src/routes/shop/shop.component.jsx @@ -1,8 +1,7 @@ import { useEffect } from 'react'; import { Routes, Route } from 'react-router-dom'; import { useDispatch } from 'react-redux'; -import { GetCategoriesAndDocuments } from '../../utils/firebase/firebase.utils'; -import { setCategories } from '../../store/category/category.action'; +import { fetchCategoriesAsync } from '../../store/category/category.action'; import CategoriesPreview from '../categories-preview/categories-preview.component'; import Category from '../category/category-component'; import './shop.styles.scss'; @@ -12,8 +11,7 @@ const Shop = () => { useEffect(() => { const getCategoriesMap = async () => { - const categoriesArray = await GetCategoriesAndDocuments(); - dispatch(setCategories(categoriesArray)) + dispatch(fetchCategoriesAsync()) } getCategoriesMap(); diff --git a/src/store/category/category.action.js b/src/store/category/category.action.js index c49fe3f..5575f8e 100644 --- a/src/store/category/category.action.js +++ b/src/store/category/category.action.js @@ -1,5 +1,21 @@ import { createAction } from "../../utils/reducer/reducer.utils" import { CATEGORIES_ACTION_TYPE } from "./category.types" +import { GetCategoriesAndDocuments } from '../../utils/firebase/firebase.utils'; +export const setCategories = (categories) => createAction(CATEGORIES_ACTION_TYPE.SET_CATEGORIES, categories) -export const setCategories = (categories) => createAction(CATEGORIES_ACTION_TYPE.SET_CATEGORIES, categories) \ No newline at end of file +export const fetchCategoriesStart = () => createAction(CATEGORIES_ACTION_TYPE.FETCH_CATEGORIES_START) +export const fetchCategoriesSuccess = (categoriesArray) => createAction(CATEGORIES_ACTION_TYPE.FETCH_CATEGORIES_SUCCESS, categoriesArray) +export const fetchCategoriesFailed = (error) => createAction(CATEGORIES_ACTION_TYPE.FETCH_CATEGORIES_FAILED, error) + + +export const fetchCategoriesAsync = () => async (dispatch) => { + dispatch(fetchCategoriesStart()) + try { + const categoriesArray = await GetCategoriesAndDocuments(); + dispatch(fetchCategoriesSuccess(categoriesArray)); + } catch (error) { + dispatch(fetchCategoriesFailed(error)); + } + +} \ No newline at end of file diff --git a/src/store/category/category.reducer.js b/src/store/category/category.reducer.js index c32ce9f..f41969e 100644 --- a/src/store/category/category.reducer.js +++ b/src/store/category/category.reducer.js @@ -1,15 +1,21 @@ import { CATEGORIES_ACTION_TYPE } from "./category.types"; const CATEGORIES_INITIAL_STATE = { - categories: [] + categories: [], + isLoading: false, + error: null } export const categoryReducer = (state = CATEGORIES_INITIAL_STATE, action = {}) => { const { type, payload } = action switch (type) { - case CATEGORIES_ACTION_TYPE.SET_CATEGORIES: - return { ...state, categories: payload } + case CATEGORIES_ACTION_TYPE.FETCH_CATEGORIES_START: + return { ...state, isLoading: true } + case CATEGORIES_ACTION_TYPE.FETCH_CATEGORIES_SUCCESS: + return { ...state, categories: payload, isLoading: false } + case CATEGORIES_ACTION_TYPE.FETCH_CATEGORIES_FAILED: + return { ...state, error: payload, isLoading: false } default: return state; } diff --git a/src/store/category/category.selector.js b/src/store/category/category.selector.js index 83924f1..9365bb0 100644 --- a/src/store/category/category.selector.js +++ b/src/store/category/category.selector.js @@ -16,4 +16,9 @@ export const categoriesMapSelector = createSelector( acc[title.toLowerCase()] = items; return acc; }, {}) +) + +export const selectCategoriesIsLoading = createSelector( + [selectCategoryReducer], + (categoriesSlice) => categoriesSlice.isLoading ) \ No newline at end of file diff --git a/src/store/category/category.types.js b/src/store/category/category.types.js index 0e6c579..d0490ac 100644 --- a/src/store/category/category.types.js +++ b/src/store/category/category.types.js @@ -1,3 +1,6 @@ export const CATEGORIES_ACTION_TYPE = { - 'SET_CATEGORIES': 'category/SET_CATEGORIES' + 'FETCH_CATEGORIES_START': 'category/FETCH_CATEGORIES_START', + 'FETCH_CATEGORIES_SUCCESS': 'category/FETCH_CATEGORIES_SUCCESS', + 'FETCH_CATEGORIES_FAILED': 'category/FETCH_CATEGORIES_FAILED' + } diff --git a/src/store/store.js b/src/store/store.js index 5998b6b..734fef1 100644 --- a/src/store/store.js +++ b/src/store/store.js @@ -1,26 +1,22 @@ import { compose, createStore, applyMiddleware } from "redux"; +import { persistStore, persistReducer } from "redux-persist"; import logger from "redux-logger"; import { rootReducer } from "./root-reducer"; +import storage from "redux-persist/lib/storage"; +import thunk from "redux-thunk"; - -const loggerMiddleware = (store) => (next) => (action) => { - console.log(store) - - if (!action.type) { - return next(action); - } - - console.log('type', action.type) - console.log('payload', action.payload) - console.log('currentState', store.getState()) - - next(action); - console.log('nextState', store.getState()) - +const persistConfig = { + key: 'root', + storage, + whitelist: ['cart'] } -const middleWares = [logger] +const persistedReducer = persistReducer(persistConfig, rootReducer); +const middleWares = [process.env.NODE_ENV === 'development' && logger, thunk].filter(Boolean) + +const composeEnhancer = (process.env.NODE_ENV !== 'production' && window && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose; +const composeEnhancers = composeEnhancer(applyMiddleware(...middleWares)) -const composeEnhancers = compose(applyMiddleware(...middleWares)) +export const store = createStore(persistedReducer, undefined, composeEnhancers) -export const store = createStore(rootReducer, undefined, composeEnhancers) \ No newline at end of file +export const persistor = persistStore(store) \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index aaf4d5b..bec867c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8186,6 +8186,16 @@ redux-logger@^3.0.6: dependencies: deep-diff "^0.3.5" +redux-persist@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-6.0.0.tgz#b4d2972f9859597c130d40d4b146fecdab51b3a8" + integrity sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ== + +redux-thunk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.2.tgz#b9d05d11994b99f7a91ea223e8b04cf0afa5ef3b" + integrity sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q== + redux@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197"