From 77f41f5613758b1b04e72f186745c96007239872 Mon Sep 17 00:00:00 2001 From: he Date: Tue, 3 Jan 2023 13:28:04 +0800 Subject: [PATCH] apply redux thunk on categories fetching logic --- src/App.js | 12 +----------- .../categories-preview.component.jsx | 8 ++++---- src/routes/shop/shop.component.jsx | 7 +++++++ src/store/categories/categories.slice.js | 19 ++++++++++++++++--- src/store/categories/categories.thunks.js | 18 ++++++++++++++++++ src/utils/firebase/firebase.utils.js | 1 + 6 files changed, 47 insertions(+), 18 deletions(-) create mode 100644 src/store/categories/categories.thunks.js diff --git a/src/App.js b/src/App.js index 68290d4..38e5bec 100644 --- a/src/App.js +++ b/src/App.js @@ -8,8 +8,6 @@ import Checkout from './routes/checkout/checkout.component'; import { onAuthStateChangeListener, createUserDocumentFromAuth } from './utils/firebase/firebase.utils'; import { useDispatch } from 'react-redux'; import { setUser } from './store/user/user.slice'; -import { getCollectionAndDocuments } from './utils/firebase/firebase.utils'; -import { setCategories } from './store/categories/categories.slice'; const App = ()=> { const dispatch = useDispatch(); @@ -23,15 +21,7 @@ const App = ()=> { }); return unsubscribe; -}, [dispatch]); - // TODO: move fetching categories logic to Shop component, and use reselector - useEffect(() => { - const getCategoriesMap = async ()=> { - const categoriesMap = await getCollectionAndDocuments(); - dispatch(setCategories(categoriesMap)); - }; - getCategoriesMap(); - }, [dispatch]) + }, [dispatch]); return ( diff --git a/src/routes/categories-preview/categories-preview.component.jsx b/src/routes/categories-preview/categories-preview.component.jsx index 999f9f9..1d523d9 100644 --- a/src/routes/categories-preview/categories-preview.component.jsx +++ b/src/routes/categories-preview/categories-preview.component.jsx @@ -3,12 +3,12 @@ import CategoryPreview from '../../components/category-preview/category-preview. import { CategoriesPreviewContainer } from './categories-preview.styles'; const CategoriesPreview = ()=> { - const categories = useSelector(state => state.categories.categories); + const categoriesMap = useSelector(state => state.categories.categoriesMap); // console.log("categories:", categories); return ( - {categories ? - Object.keys(categories).map((title)=>( - + {categoriesMap ? + Object.keys(categoriesMap).map((title)=>( + )) :
no products data!
}
diff --git a/src/routes/shop/shop.component.jsx b/src/routes/shop/shop.component.jsx index e3ba998..ef7d29a 100644 --- a/src/routes/shop/shop.component.jsx +++ b/src/routes/shop/shop.component.jsx @@ -1,8 +1,15 @@ +import { useEffect } from 'react'; import { Routes, Route } from 'react-router-dom'; import CategoriesPreview from '../categories-preview/categories-preview.component'; import Category from '../category/category.component'; +import { getCategories } from '../../store/categories/categories.thunks'; +import { useDispatch } from 'react-redux'; const Shop = ()=> { + const dispatch = useDispatch(); + useEffect(()=> { dispatch(getCategories()); } + , [dispatch]) + return ( } /> diff --git a/src/store/categories/categories.slice.js b/src/store/categories/categories.slice.js index fd378b3..2ac074c 100644 --- a/src/store/categories/categories.slice.js +++ b/src/store/categories/categories.slice.js @@ -1,18 +1,31 @@ import { createSlice } from "@reduxjs/toolkit"; const InitCategoriesSate = { - categories: {}, + categoriesMap: null, + isLoading: false, + error: null }; export const categoriesSlice = createSlice({ name: 'categories', initialState: InitCategoriesSate, reducers: { + getCategoriesStart: (state) => { + state.isLoading = true; + state.error = null; + }, + // getCategoriesSucceeded setCategories: (state, action) => { - state.categories = action.payload; + state.categoriesMap = action.payload; + state.isLoading = false; + state.error = null; + }, + getCategoriesFailed: (state, action) => { + state.isLoading = false; + state.error = action.payload; }, } }); -export const { setCategories } = categoriesSlice.actions; +export const { getCategoriesStart, setCategories, getCategoriesFailed } = categoriesSlice.actions; export default categoriesSlice.reducer; \ No newline at end of file diff --git a/src/store/categories/categories.thunks.js b/src/store/categories/categories.thunks.js new file mode 100644 index 0000000..f91bfb1 --- /dev/null +++ b/src/store/categories/categories.thunks.js @@ -0,0 +1,18 @@ +import { getCollectionAndDocuments } from '../../utils/firebase/firebase.utils'; +import { getCategoriesStart, setCategories, getCategoriesFailed } from './categories.slice'; + +// redux thunk arrow function +export const getCategories = ()=> async (dispatch, getState)=>{ + const state = getState(); + if (state.categories.categoriesMap) { + return // if categories data in redux already persist in local, don't fetch new and return. + } + dispatch(getCategoriesStart()); + // console.log("fire getCategories"); + try { + const categoriesMap = await getCollectionAndDocuments(); + dispatch(setCategories(categoriesMap)); + } catch (error) { + dispatch(getCategoriesFailed(error.message)); + } +}; \ No newline at end of file diff --git a/src/utils/firebase/firebase.utils.js b/src/utils/firebase/firebase.utils.js index 88db436..9fcc681 100644 --- a/src/utils/firebase/firebase.utils.js +++ b/src/utils/firebase/firebase.utils.js @@ -58,6 +58,7 @@ export const getCollectionAndDocuments = async () => { const collectionRef = collection(db, 'categories'); const q = query(collectionRef); const querySnapshot = await getDocs(q); + if ( querySnapshot.empty ) { throw new Error("can't get data, offline?"); } const categoriesMap = querySnapshot.docs.reduce((accm, doc)=>{ const { title, items } = doc.data(); accm[title.toLowerCase()] = items;