1 year ago
#345513
Alex Lebedev
How to initialize redux store initialState for a dynamic component
I have an e-commerce Next.JS app with 3 different product types:
1. Fully custom product with dynamic props
2. Partly custom
3. Static with already defined props
Parent component - "detail product view" which contains a lot of different child components. There are about 7 different variations of a products.
I connected Next.JS with redux store (redux-toolkit).
Child components receive initial state data from:
CMS API -> Next.JS page props -> Child components data -> ? redux store ?
Seems like I'm missing something here...
My main question is:
How to initialize all the child components initialStates for that parent "detail product view" component in the redux store?
For fully custom products I want redux store to have some default product props with first component render and all others to be null till user choose the right one (so I can dispatch them directly to the product store). For static products I want redux store to have them already defined with first component render.
I've read about redux batch, but in my guts I don't think it would be a good idea.
Some more details:
I have a parent component called 'product-details':
import {
useReducer,
} from "react";
import { IoCart } from "react-icons/io5";
import ProductPrice from "./ProductPrice";
import ProductInStock from "./ProductInStock";
import ProductCount from "./ProductCount";
import ProductMousse from "./ProductMousse";
import ProductWeight from "./ProductWeight";
import ProductSize from "./ProductSize";
import ProductColors from "./ProductColors";
import ProductFlavors from "./ProductFlavors";
import ProductAddons from "./ProductAddons";
import Note from "../note/Note";
import Button from "../buttons/Button";
import types from "./productTypes.json";
import ProductSpecialRequest from "./ProductSpecialRequest";
import { useDispatch, useSelector } from "react-redux";
import ProductQuantity from "./ProductQuantity";
import ProductPickupDate from "./ProductPickupDate";
import { genHash } from "../../util/index";
import styles from "./ProductDetails.module.scss";
export default function ProductDetails({
productInfo,
productType = types.POPULAR,
}) {
const product =
productType === types.SPECIAL ? productInfo : productInfo.type[0];
const productQuantity = useSelector(
(state) => state.tempProduct.productQuantity
);
const productCount = product.count;
const productSets = product.sets;
const productColors = product.colors || product.color;
const productFlavors = product.flavors || product.flavor;
const productSize = product.productSizes || product.productSize;
const productMousse = product.mousses || product.mousse;
const productSpecialRequest = productInfo.special_request_available
? productInfo.special_request_available
: false;
const productInStock =
productType !== types.AVAILABLE_TODAY || productInfo.inStockCount > 0
? true
: false;
I need to get rid of the local state below and push known props directly to redux store, so I'm asking what is the best way to do it?
const initialState = {
productId: null,
productType: productType,
name: product.name,
pickupDate: null,
count: productCount ? productCount : null,
mousse: null,
size: null,
shape: null,
weight: null,
colors: null,
flavors: null,
addons: null,
specialFee: {
state: productSpecialRequest,
specialRequestField: null,
price: null,
},
quantity: 1,
totalProductPrice: 0,
};
const [state, setState] = useReducer(
(state, newState) => ({ ...state, ...newState }),
initialState
);
return (
<div className={styles.ProductDetails}>
<div className={styles.ProductDetails__header}>
<h2 className={styles.ProductDetails__header__title}>
{product.name.toUpperCase()}
</h2>
</div>
<div className={styles.ProductDetails__header__info}>
{productType === types.AVAILABLE_TODAY ? (
<ProductPrice
productPrice={product.price}
countable={+productCount}
/>
) : (
<ProductPrice
productPrice={totalProductPrice}
countable={+productCount}
/>
)}
<ProductInStock
inStockCount={productInfo.inStockCount}
productType={productType}
/>
</div>
<div className={styles.ProductDetails__section}>
<span className={styles.ProductDetails__title}>Description</span>
<p className={styles.ProductDetails__description}>
{product.description}
</p>
</div>
<form onSubmit={handleProductFormSubmit}>
<ProductPickupDate
productType={productType}
onPickupDateChange={handlePickupDateChange}
/>
{product.weight && (
<ProductWeight weight={product.weight} productType={productType} />
)}
{product.size && (
<ProductSize
size={productSize}
productType={productType}
onSizeChange={handleSizeChange}
onShapeChange={handleShapeChange}
/>
)}
<ProductCount count={productCount} productType={productType} />
<ProductColors colors={productColors} productType={productType} />
<ProductMousse
mousse={productMousse}
productType={productType}
onMousseChange={handleMousseChange}
/>
<ProductFlavors flavors={productFlavors} productType={productType} />
<ProductAddons
addons={product.addons}
productType={productType}
onChange={handleAddonsChange}
/>
<ProductQuantity
productQuantity={productQuantity}
heading={true}
maxQuantity={
productType === types.AVAILABLE_TODAY
? product.inStockCount
: MAX_PRODUCT_QUANTITY
}
onQuantityChange={handleProductQuantity}
/>
{productSpecialRequest && (
<ProductSpecialRequest
specialRequestState={state.specialFee}
onChange={handleSpecialFeeChange}
/>
)}
</form>
<div className={styles.ProductDetails__section}>
<div className={styles.ProductDetails__footer}>
<Button
onClick={handleAddToCart}
text={`Add to Cart $${totalProductPrice}`}
style="Secondary"
icon={<IoCart />}
fullWidth={true}
disabled={!productInStock}
/>
<Button
onClick={handleAddToCart}
text={`Buy Now $${totalProductPrice}`}
fullWidth={true}
link="/cart"
disabled={!productInStock}
/>
</div>
</div>
</div>
);
}
I created slice of a temp cart product like this: (for now it's just a structure of a product and a template for future actions)
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
productId: null,
productDetails: {
productType: null,
productName: null,
productPickupDate: null,
productCount: null,
productMousse: null,
productSize: null,
productSet: null,
productShape: null,
productWeight: null,
productColors: [],
productFlavors: [],
productAddons: [],
},
specialFee: {
state: null,
specialRequestField: null,
price: null,
},
productQuantity: 1,
totalProductPrice: 0,
};
const tempProductSlice = createSlice({
name: "tempProduct",
initialState,
reducers: {
setProductId(state, action) {
state.productId = action.payload;
},
setProductType(state, action) {
state.productDetails.productType = action.payload;
},
setProductName(state, action) {
state.productDetails.productName = action.payload;
},
setProductPickupDate(state, action) {
state.productDetails.productPickupDate = action.payload;
},
setProductCount(state, action) {
state.productDetails.productCount = action.payload;
},
setProductMousse(state, action) {
state.productDetails.productMousse = action.payload;
},
setProductSize(state, action) {
if (state.productDetails.productType !== "available-today") {
totalProductPrice += action.payload.price;
}
state.productDetails.productSize = action.payload;
},
setProductShape(state, action) {
state.productDetails.productShape = action.payload;
},
setProductSet(state, action) {
state.productDetails.productSet = action.payload;
},
setProductWeight(state, action) {
state.productDetails.productWeight = action.payload;
},
addColor(state, action) {
state.productDetails.productColors.push(action.payload);
},
removeColor(state, action) {
state.productDetails.productColors =
state.productDetails.productColors.filter(
(color) => color.name !== action.payload.name
);
},
addFlavor(state, action) {
state.productDetails.productFlavors.push(action.payload);
},
removeFlavor(state, action) {
state.productDetails.productFlavors =
state.productDetails.productFlavors.filter(
(flavor) => flavor.name !== action.payload.name
);
},
addAddon(state, action) {},
removeAddon(state, action) {},
setProductSpecialRequestFee(state, action) {
state.productDetails.specialFee = action.payload;
},
increaseProductQuantity(state) {
state.productQuantity += 1;
},
decreaseProductQuantity(state) {
state.productQuantity -= 1;
},
clearProduct(state) {
state.tempProduct = {};
},
},
});
export const {
increaseProductQuantity,
decreaseProductQuantity,
clearProduct,
} = tempProductSlice.actions;
export default tempProductSlice.reducer;
javascript
redux
react-redux
next.js
redux-persist
0 Answers
Your Answer