Search code examples
javascriptreactjsreduxreact-redux

"TypeError: Cannot destructure property 'product' of 'productDetails' as it is undefined"


I am currently trying to display product data on the product screen. This is the error I'm getting in the console when opening a product details screen.

TypeError: Cannot destructure property 'product' of 'productDetails' as it is undefined.

I'm not sure why productDetails is undefined. It should contain product details data.

Here is my code.

ProductScreen.js

import React, { useEffect } from 'react';
import { useParams } from "react-router-dom";
import {Link} from "react-router-dom";
import { useSelector, useDispatch } from 'react-redux';
import { detailsProduct } from "../actions/productActions"

function ProductScreen() {
    const { id } = useParams();
    
    const productDetails = useSelector(state => state.productDetails);
    const {product, loading, error} = productDetails;
    const dispatch = useDispatch; 
    console.log("something");

    useEffect(() => {
        dispatch(detailsProduct(id));
        return () => {
            //
        };
    }, []);

    return <div>
        <div className="back-to-results">
            <Link to="/">Back to results</Link>
        </div>
        {loading ? <div>Loading...</div> :
        error ? <div>{error}</div> :
        (
            <div className="details">
            <div className="details-image">
                <img src={product.image} alt="product" ></img>
            </div>
            <div className="details-info">
                <ul>
                    <li>
                        <h4>{product.name}</h4>
                    </li>
                    <li>
                        {product.rating} Stars ({product.numReviews} Reviews)
                    </li>
                    <li>
                        {product.price}
                    </li>
                    <li>
                        Description:
                        <div>
                            {product.description}
                        </div>
                    </li>
                </ul>
            </div>
            <div className="details-action">
                <ul>
                    <li>
                        Price: <b>${product.price}</b>
                    </li>
                    <li>
                        Status: {product.status}
                    </li>
                    <li>
                        Qty: <select>
                            <option>1</option>
                            <option>2</option>
                            <option>3</option>
                            <option>4</option>
                            <option>5</option>
                        </select>
                    </li>
                    <li>
                        <button className="button">Add to Cart</button>
                    </li>
                </ul>
            </div>
        </div>
        )
}
    </div>
}
export default ProductScreen;

productActions.js

import { PRODUCT_LIST_FAIL, PRODUCT_LIST_REQUEST, PRODUCT_LIST_SUCCESS, PRODUCT_DETAILS_REQUEST, PRODUCT_DETAILS_SUCCESS, PRODUCT_DETAILS_FAIL } from "../constants/productconstants.js";
import axios from "axios";

const listProducts = () => async (dispatch) => {

    try {
        dispatch({type: PRODUCT_LIST_REQUEST});
        const {data} = await axios.get("/api/products");
        dispatch({type: PRODUCT_LIST_SUCCESS, payload: data});
    }
    catch (error) {
        dispatch({type: PRODUCT_LIST_FAIL, payload:error.message});
    }
}

const detailsProduct = (productId) => async (dispatch) => {
    try {
        dispatch({type: PRODUCT_DETAILS_REQUEST, payload: productId});
        const {data} = await axios.get("/api/products/" + productId);
        dispatch({type: PRODUCT_DETAILS_SUCCESS, payload: data});
    }
    catch (error) {
        dispatch({type: PRODUCT_DETAILS_FAIL, payload: error.message});
    }
}

export {listProducts, detailsProduct};

Solution

  • Initially when the page is rendered productDetails state should be empty or undefined although you have dispatched detailsProduct in useEffect but it will take some time to get data from API request that's why you are getting the error. You can use productDetails without destructure

    productDetails?.loading
    productDetails?.product?.name
    

    the question mark between productDetails and loading is Optional Chaining you can read more: Optional Chaining

    **

    Updated

    **

    function ProductScreen() {
      const { id } = useParams();
    
      const productDetails = useSelector((state) => state.productDetails);
      const data = productDetails;
      const dispatch = useDispatch();
      console.log("something", data);
    
      useEffect(() => {
        dispatch(detailsProduct(id));
        return () => {
          //
        };
      }, []);
    
      useEffect(() => {
        console.log(productDetails)
      }, [productDetails]);
    
      return (
        <div>
          <div className="back-to-results">
            <Link to="/">Back to results</Link>
          </div>
          {data?.loading ? (
            <div>Loading...</div>
          ) : data?.error ? (
            <div>{data?.error}</div>
          ) : (
            <div className="details">
              <div className="details-image">
                <img src={data?.product?.image} alt="product"></img>
              </div>
              <div className="details-info">
                <ul>
                  <li>
                    <h4>{data?.product?.name}</h4>
                  </li>
                  <li>
                    {data?.product?.rating} Stars 
                    {data?.product?.numReviews} Reviews
                  </li>
                  <li>{data?.product?.price}</li>
                  <li>
                    Description:
                    <div>{data?.product?.description}</div>
                  </li>
                </ul>
              </div>
              <div className="details-action">
                <ul>
                  <li>
                    Price: <b>${data?.product?.price}</b>
                  </li>
                  <li>Status: {data?.product?.status}</li>
                  <li>
                    Qty:{" "}
                    <select>
                      <option>1</option>
                      <option>2</option>
                      <option>3</option>
                      <option>4</option>
                      <option>5</option>
                    </select>
                  </li>
                  <li>
                    <button className="button">Add to Cart</button>
                  </li>
                </ul>
              </div>
            </div>
          )}
        </div>
      );
    }
    export default ProductScreen;