1

I am trying to load a users' cart upon authentication using axios in my react app but it returns an error saying "cannot read properties of undefined(reading 'data')". The error highlights my cartAction.js:15 which is a .catch method that returns the error response data and status but when i console.log the response from my axios.get(/api/cart/${id}) it logs the response successfully in my console but it doesn't render in my react app. I think the problem is from my Cart.js component code but i can't seem to rectify it. The console.log(err) in my cartAction.js getCart constant printed "Error: Checkout(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null." in my console

Here is my cartAction.js code

> export const getCart = (id) => dispatch => { > dispatch(setCartLoading()); > axios.get(`/api/cart/${id}`) > .then(res => dispatch({ > type: GET_CART, > payload: res.data > }) > ) > .catch(err => { > console.log(err) > dispatch(returnErrors(err.response.data, err.response.status))}); } > > export const addToCart = (id, productId, quantity) => dispatch => { > axios.post(`/api/cart/${id}`, {productId, quantity}) > .then(res => dispatch({ > type: ADD_TO_CART, > payload: res.data > })) > .catch(err => dispatch(returnErrors(err.response.data, err.response.status))); } > > export const deleteFromCart = (userId, itemId) => dispatch => { > axios.delete(`/api/cart/${userId}/${itemId}`) > .then(res => dispatch({ > type: DELETE_FROM_CART, > payload: res.data > })) > .catch(err => dispatch(returnErrors(err.response.data, err.response.status))); } > > export const setCartLoading = () => { > return{ > type: CART_LOADING > } } 

Here is my component/Cart.js code

class Cart extends Component{ state = { loaded: false } static propTypes = { getCart: PropTypes.func.isRequired, isAuthenticated: PropTypes.bool, addToCart: PropTypes.func.isRequired, deleteFromCart: PropTypes.func.isRequired, user: PropTypes.object.isRequired, cart: PropTypes.object.isRequired, checkout: PropTypes.func.isRequired } getCartItems = async (id) => { await this.props.getCart(id); this.state.loaded = true; } onDeleteFromCart = (id, itemId) => { this.props.deleteFromCart(id, itemId); } render(){ const user = this.props.user; if(this.props.isAuthenticated && !this.props.cart.loading && !this.state.loaded){ this.getCartItems(user._id); } return( <div> <AppNavbar/> {this.props.isAuthenticated ? <Fragment> { this.props.cart.cart ? null : <Alert className="text-center" color="info">Your cart is empty!</Alert> } </Fragment> : <Alert className="text-center" color="danger">Login to View</Alert> } {this.props.isAuthenticated && !this.props.cart.loading && this.state.loaded && this.props.cart.cart ? <Container> <div className="row"> {this.props.cart.cart.items.map((item)=>( <div className="col-md-4"> <Card> <CardBody> <CardTitle tag="h5">{item.name}</CardTitle> <CardSubtitle>NGN {item.price}</CardSubtitle> <CardText>Quantity - {item.quantity}</CardText> <Button color="danger" onClick={this.onDeleteFromCart.bind(this, user._id, item.productId)}>Delete</Button> </CardBody> </Card> <br/> </div> ))} <div className="col-md-12"> <Card> <CardBody> <CardTitle tag="h5">Total Cost = NGN. {this.props.cart.cart.bill}</CardTitle> <Checkout user={user._id} amount={this.props.cart.cart.bill} checkout={this.props.checkout} /> </CardBody> </Card> </div> </div> </Container> : null } </div> ); } } const mapStateToProps = (state) => ({ cart: state.cart, isAuthenticated: state.auth.isAuthenticated, user: state.auth.user }) export default connect(mapStateToProps, {getCart, deleteFromCart, checkout})(Cart); 

Here's my cartReducer.js code

const initialState = { cart: null, loading: false } export default function cartReducer (state=initialState, action){ switch(action.type){ case GET_CART: return { ...state, cart: action.payload, loading: false } case ADD_TO_CART: return { ...state, cart: action.payload } case DELETE_FROM_CART: return { ...state, cart: action.payload } case CART_LOADING: return { ...state, loading: true } default: return state; } } 
2
  • I don't see where you access any *.data property in your UI code, but you do in several places in your action creators in cartAction file. Which line is line 15? My guess is that the error object doesn't have the properties you are accessing into, i.e. err.response is undefined. Can you update your question to include the result of console.log(err)? Commented Oct 24, 2021 at 22:35
  • i've updated it with the console.log(err) result Commented Oct 24, 2021 at 23:28

1 Answer 1

0

I think I see the issue now. You are issuing a side-effect from your render method. render is to be considered a synchronous, pure function. If you need to fetch data it should be in one of the component lifecycle methods, i.e. componentDidMount and/or componentDidUpdate.

Move the "getCartItems" conditional logic into a utility function and invoke from the componentDidMount lifecycle method.

fetchCartItems = () => { const { cart, isAuthenticated, user } = this.props; const { loaded } = this.state; if (isAuthenticated && !cart.loading && !loaded) { this.getCartItems(user._id); } } componentDidMount() { this.fetchCartItems(); } ... render() { const { user } = this.props; return ( <div> ... </div> ); } 

And if there's a chance you may need to again fetch the cart items later after the component has mounted, for example, the authentication status changes, use the componentDidUpdate method.

componentDidUpdate() { this.fetchCartItems(); } 
Sign up to request clarification or add additional context in comments.

8 Comments

I did this and thankfully I didn't get the error again but now when I add items to my cart and load my cart it always respond with the "Your cart is empty" alert. Also when I moved the getCartItems conditional logic from the render method into the utility function I had to change the user._id to this.props.user_id or else it user becomes undefined
@Fransco You may want to wait to render the empty cart alert until you've loaded the state fully, i.e. wait until this.state.loaded is true.
I tried this but my cart still shows up empty
@Fransco I don't know what your redux state looks like, but are you sure your cart is populated? Can you add your reducer code to your question? And if possible think you could create a running codesandbox example of your code we can inspect and debug live?
I've updated the question to include my cart reducer code
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.