TypeError: Cannot read property 'length' of undefined in Commerce JS when accessing cart

Viewed 1279

When Accessing the line_items array of the cart object in commerce js, i get an error saying that it is undefined, while I can still see it in the console.

Here is some screenshots:

Error:

TypeError: Cannot read property 'length' of undefined
Cart
D:/Zayeed/Projects/e_commerce/src/components/Cart/Cart.jsx:7
   4 | 
   5 | const Cart = ({ cart }) => {
   6 |     const classes = useStyles();
>  7 |     const isEmpty = !cart.line_items.length;
   8 | 
   9 |     const EmptyCart = () => (
  10 |         <Typography variant="subtitle1">You have no items in your cart, start adding some.</Typography

My Code:

import React from 'react'
import { Container, Typography, Button, Grid } from '@material-ui/core'
import useStyles from './styles';

const Cart = ({ cart }) => {
    const classes = useStyles();
    const isEmpty = !cart.line_items.length;

    const EmptyCart = () => (
        <Typography variant="subtitle1">You have no items in your cart, start adding some.</Typography>
    );

    const FilledCart = () => {
        <>
            <Grid container spacing={3}>
                {cart.line_items.map(item => (
                    <Grid item xs={12} sm={4} key={item.id}>
                        <div>{item.name}</div>
                    </Grid>
                ))}
            </Grid>

            <div className={classes.cardDetails}>
                    <Typography variant="h4">Subtotal: {cart.subtotal.formatted_with_symbol}</Typography>
                    <div>
                        <Button className={classes.emptyButton} size="large" type="button" variant="contained" color="secondary">Empty Cart</Button>
                        <Button className={classes.checkout} size="large" type="button" variant="contained" color="primary">Checkout</Button>
                    </div>
            </div>
        </>
    }

    return (
        <Container>
            <div className={classes.toolbar}/>
            <Typography className={classes.title} variant="h3">Your Shopping Cart:</Typography>
            {isEmpty ? <EmptyCart/> : <FilledCart />}
        </Container>
    )
}

export default Cart

Console Log of the cart object:

App.js:

import React, { useState, useEffect } from 'react'
import { commerce } from './lib/commerce'
import { Products, Navbar, Cart } from './components'

const App = () => {
    const [ products, setProducts ] = useState([])
    const [cart, setCart] = useState({})

    const fetchProducts = async() => {
        const { data } = await commerce.products.list()

        setProducts(data)
    }

    const fetchCart = async() => {
        setCart(await commerce.cart.retrieve())
    }

    const handleAddToCart = async(productId, quantity) => {
        const item = await commerce.cart.add(productId, quantity)

        setCart(item.cart)
    }

    useEffect(() => {
        fetchProducts()
        fetchCart()
    }, []);

    return (
        <div>
            <Navbar  totalItems={cart.total_items}/>
            {/* <Products products={products} onAddToCart={handleAddToCart}/> */}
            <Cart cart={cart}/>
        </div>
    )   
}

export default App

It seems like the prop isn't imported as it is supposed to be, I can't access any property of cart object.

Thanks in advance!

4 Answers

Try const isEmpty = Object.keys(cart).length && !cart.line_items.length; The data is not received form the server but the component is rendered and that is why at first the card is undefined.

This is happens because cart content doesnt load from e-commerce api immediately when page open and then because of the error it stuck like that.

You can first check is there a cart content first, and delete isEmpty variable and just write in javascript closure, in the video explained a few seconds later, but after a day of struggling, I realized it too :D

if(!cart.line_items)
    return  '...loading';


return (
    <div className={classes.toolbar}>
        <Typography className={classes.title} variant="h3"> Your Shopping Cart 
        </Typography>
        {!cart.line_items.length ? <EmptyCart /> : <FilledCart />}
       
    </div>
)

Try using optional chaining:

const isEmpty = !cart.line_items?.length

This will ask for the array length only if the array exists, preventing undefined errors. That means it will return false either if cart.line_items == undefined, or if cart.line_items.length == 0.

Maybe i'm a bit late to the party, but i got stuck in the same part and this solved my issue (and also i think it looks pretty neat)

Issue There is no cart.line_items on the initial render since initial state is an empty object ({}).

const [cart, setCart] = useState({});

Solution Provide valid initial state for the initial render so there’s a truthy, defined cart.line_items object from which to have a length property, i.e. so !cart.line_items.length; can resolve to a value and not throw an error.

const [cart, setCart] = useState({ line_items: [] });

Related