1 year ago

#345513

test-img

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

Accepted video resources