How can make user chosen category persist ?
store.js
import { combineReducers, configureStore } from "@reduxjs/toolkit"
import {
persistReducer,
FLUSH,
REHYDRATE,
PAUSE,
PERSIST,
PURGE,
REGISTER,
} from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import adminAdvertisementReducer from "./features/admin/advertisementSlice"
import usersReducer from "./features/users/usersSlice"
import cartReducer from "./features/cart/cartSlice"
import ordersReducer from "./features/orders/ordersSlice"
import productsReducer from "./features/products/productsSlice"
const persistConfig = {
key: "root",
version: 1,
storage
}
const **rootReducer** = combineReducers({
cart: cartReducer,
users: usersReducer,
// products: productsReducer
})
const persistedReducer = persistReducer(persistConfig, rootReducer)
export const **store** = configureStore({
reducer: {
persistedReducer,
adminMainAdvertisement: adminAdvertisementReducer,
orders: ordersReducer,
products: productsReducer
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
}),
})
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from "react-redux";
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {store} from "./store";
import { PersistGate } from 'redux-persist/integration/react';
import persistStore from 'redux-persist/es/persistStore';
let persistor = persistStore(store)
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor} >
<App />
</PersistGate>
</Provider>
</React.StrictMode>
);
productsSlice.js
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import apiAxios from "../../config/axiosConfig";
export const fetchAllProducts = createAsyncThunk("products/fetchAllProducts", async () => {
const response = await apiAxios.get("/products")
const products = {}
response.data.forEach(product => {
products[product.id] = product
})
return products
})
export const fetchProductsByCategory = createAsyncThunk("products/fetchProductsByCategory", async (category) => {
const response = await apiAxios.get(`/products/${category}`)
return response.data
})
export const productsSlice = createSlice({
name: "products",
initialState: {
allProducts: {},
productsInCategory: [],
categoryTheme: "",
**fetchAllProductsStatus: "idle",**
fetchProductsByCategoryStatus: "idle"
},
reducers: {
setCategoryTheme(state, action) {
state.categoryTheme = action.payload
}
},
extraReducers: (builder) => {
// Reduces for fetching products
builder
.addCase(fetchAllProducts.pending, (state, action) => {
state.fetchAllProductsStatus = "loading"
})
.addCase(fetchAllProducts.fulfilled, (state, action) => {
state.fetchAllProductsStatus = "succeeded"
state.allProducts = action.payload
})
.addCase(fetchAllProducts.rejected, (state, action) => {
state.fetchAllProductsStatus = "failed"
})
// fetchProductsByCategory
.addCase(fetchProductsByCategory.pending, (state, action) => {
state.fetchProductsByCategoryStatus = "loading"
})
.addCase(fetchProductsByCategory.fulfilled, (state, action) => {
state.fetchProductsByCategoryStatus = "succeeded"
state.productsInCategory = action.payload
})
.addCase(fetchProductsByCategory.rejected, (state, action) => {
state.fetchProductsByCategoryStatus = "failed"
})
}
})
export const { setCategoryTheme } = productsSlice.actions
export const selectAllProducts = state => state.products.allProducts
export const selectProductsInCategory = state => state.products.productsInCategory
export const selectProductById = (state, productId) => state.products.allProducts[productId]
export const selectCategoryTheme = state => state.products.categoryTheme
**export const selectFetchAllProductsStatus = state => state.products.fetchAllProductsStatus**
export const selectFetchProductsByCategoryStatus = state => state.products.fetchProductsByCategoryStatus
export default productsSlice.reducer
dropDownTab.js
import { Link, useHistory } from "react-router-dom";
import "../nav/nav.css"
import { useDispatch, useSelector } from "react-redux";
import { fetchProductsByCategory, selectFetchProductsByCategoryStatus, setCategoryTheme } from "../../features/products/productsSlice";
import { useEffect, useState } from "react";
const DropdownTab = ({name, items}) => {
const dispatch = useDispatch()
const history = useHistory()
const fetchProductsByCategoryStatus = useSelector(selectFetchProductsByCategoryStatus)
const [category, setCategory] = useState("")
// Sorting items a-z
const ascItems = items.sort((a, b) => {
return a.localeCompare(b)
})
useEffect(() => {
if (fetchProductsByCategoryStatus === "succeeded") {
history.push(`/products/category/${category}`)
}
}, [history, fetchProductsByCategoryStatus, category])
const handleClick = (item) => {
setCategory(item)
dispatch(setCategoryTheme(item))
dispatch(fetchProductsByCategory(item))
}
return (
<div class="dropdown" >
<span class="dropdown-toggle navItemMd" data-bs-toggle="dropdown" aria-expanded="false">
{name}
</span>
** <ul className="dropdown-menu navItemMd categoryTab text-decoration-none" autoClose="false">
{
ascItems
? ascItems.map((item) => {
return (
<li key={item} className="active">
<Link to="" className="dropdown-item" onClick={() => handleClick(item)}>{item}</Link>
</li>
)
})
: <span>Category is empty</span>
}
</ul>**
</div>
);
}
export default DropdownTab;
Category.js
import "./category.css";
import { useDispatch, useSelector } from "react-redux";
import {
fetchProductsByCategory, selectCategoryTheme, selectFetchProductsByCategoryStatus, selectProductsInCategory
} from "../../features/products/productsSlice";
import { Link, useParams } from "react-router-dom";
import { useEffect, useState } from "react";
const ProductCategory = () => {
const dispatch = useDispatch()
// const categoryUrl = useParams().category
const fetchProductsByCategoryStatus = useSelector(selectFetchProductsByCategoryStatus)
const productsInCategory = useSelector(selectProductsInCategory)
const categoryTheme = useSelector(selectCategoryTheme)
const [category, setCategory] = useState("")
console.log("category.js ", category, categoryTheme, fetchProductsByCategoryStatus, productsInCategory)
useEffect(() => {
if (fetchProductsByCategoryStatus === "succeeded") {
setCategory(productsInCategory[0].category)
} else if (fetchProductsByCategoryStatus === "idle" && categoryTheme) {
console.log("2")
dispatch(fetchProductsByCategory(categoryTheme))
} else {
console.log("test")
}
}, [fetchProductsByCategoryStatus, categoryTheme, category])
return (
<div className="container p2" style={{minHeight:"100vh"}}>
<nav aria-label="breadcrumb">
<ol class="breadcrumb" style={{fontFamily:"serif", fontSize:"0.6rem", marginTop:"0.4rem", marginBottom:"0.4rem"}}>
<li class="breadcrumb-item"><Link to ="/">Home</Link></li>
<li class="breadcrumb-item active" aria-current="page">Category</li>
</ol>
</nav>
<div style={{fontFamily:"serif"}}>{category}</div>
**<div className="d-md-flex justify-content-center align-items-center">
{
productsInCategory.length <= 0
? <p>This category is currently empty.</p>
: productsInCategory.map(item => {
return (
<div key={item.id} className="card shadow m-md-2 my-2 col-12 col-md-4">
<img src={item.img_url} class="card-img-top" alt="item in category" />
<div class="card-body setCardBody">
<p class="card-title"><b>Name: </b><span>{item.title}</span></p>
<div>
<span><b>Price: $ </b>{item.price}</span>
<span> | <b>Status: </b>{item.status}</span>
</div>
<p class="card-text"><b>Description: </b><span>{item.description}</span></p>
<button class="btn btn-primary setCardBody">Add to cart</button>
</div>
</div>
)})
}
</div> **
</div>
);
}
export default ProductCategory;
in store.js, I have tried to put productsReducer in rootReducer and not in store. But it results in error: undefined fetchAllProductsStatus. If I commented out the productsReducer in rootReducer and put it in store, user can choose the category, but the error in the title emerges.
info: user chooses category in dropDownTab.js and the page goes to category.js. If category page is refreshed, user input is deleted automatically (This is the problem).
products: productsReducerstate? Please edit to clarify what the exact specific problem is. If there is an error, include the complete error message and any accompanying stacktrace.