3

Working my way through my first fullstack MERN app that allows a user to add multiple places to an array. Trying to map over the array to display the number of places for the user. Code works fine until I use .length, then it crashes.

import React from 'react';

import UserItem from './UserItem';
import Card from '../../shared/components/UIElements/Card/Card';
import './UsersList.css';


const UsersList = props => {
    if (props.items.length === 0) {
        return (
            <div className="center">
                <Card>
                    <h2>No users found.</h2>
                </Card>
            </div>
        );
    }


    return (
        <ul className="users-list">
            {props.items.map(user => (
                <UserItem
                    key={user.id}
                    id={user.id}
                    image={user.image}
                    name={user.name}
                    // placeCount={user.places.length}
                    placeCount={user.places}
                />
            ))}
        </ul>
    );


};

export default UsersList;

The full error stack:

Uncaught TypeError: Cannot read properties of undefined (reading 'length')
The above error occurred in the <UsersList> component:

  
Consider adding an error boundary to your tree to customize error handling behavior.

Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
  

I did a keyword search of .length throughout the backend and frontend files to double check the code and make sure I didn't have any typos anywhere--but for the life of me I can't figure out why adding .length isn't working.

I've gone through a few different posts on here but cannot find a working solution.

Any insight would be greatly appreciated. For now I just commented out .length so I can keep working. Thank you!

kRiZ
  • 2,320
  • 4
  • 28
  • 39
SJo
  • 33
  • 1
  • 7

4 Answers4

2

You can define a default value for items to fix it.

const UsersList = ({ items = [] }) => {
  if (items.length === 0) {
    return (
      ...
    );
  }

  return (
    <ul className="users-list">
      {items.map((user) => (
        ...
      ))}
    </ul>
  );
};
h7n
  • 99
  • 3
  • Thanks for responding so quickly! I just tried this and I'm still getting the same error for some reason – SJo Jan 03 '23 at 03:54
2

Changing this if condition from this:

if (props.items.length === 0) {

To

if (!props.items?.length) {

Should work.

Similarly if using for places,you can check using ternary operator if length exists in array of places.

How? Because items could possibly be null or undefined from apis, so the length property on array might be missing.

Moreover, using ! Not operator would make this condition true if length of items is 0 or is null or undefined or any falsy value

Murtaza Hussain
  • 3,851
  • 24
  • 30
  • Thanks for responding- just tried this and I'm still getting the same error for some reason – SJo Jan 03 '23 at 03:50
2

The other answers are focusing on prop.items list but in your question you mention that you get the exception when trying to access the length of the places inside users.

You are getting the error message because some users may not have any places listed inside them, hence, no places list -> no length property.

To fix this, you need to check if the places list exists and then access its length:

placeCount={ user.places ? user.places.length : 0 }

Here, we use a ternary operator to check if user.places exists, then we use the length, else use zero.

Edit: As pointed out by Phil in the comments below, you can also use coalescing operator, which is much cleaner, like this:

placeCount={ user.places?.length ?? 0 }

The syntax simply translates as if user.places has a value for length, then use that value, else use zero as a default value.

kRiZ
  • 2,320
  • 4
  • 28
  • 39
  • 1
    Could also use `user.places?.length ?? 0` – Phil Jan 03 '23 at 03:50
  • THANK YOU BOTH- I just tried both and they BOTH work!! I've been stuck on this for HOURS trying to backtrack and figure out what I was doing wrong. You both just alleviated so much stress! I appreciate you both taking the time to provide a working solution and explanation in the process--- I'm a newbie, trying to learn and I appreciate the kind folks like you both who really try to help. THANK YOU SO MUCH!! – SJo Jan 03 '23 at 04:34
1

Here you can use object destructuring. if you are not sure whether the data will come or not. JSX will always render first and after that props, so in some cases it may render empty JSX.

you can make the changes in your component like this

const UsersList = props => {
const { items = [] } = props

    // checking whether its an array or not and have some items in it
    if (!Array.isArray(items) || items.length === 0) {
        return (
            <div className="center">
                <Card>
                    <h2>No users found.</h2>
                </Card>
            </div>
        );
    }


    return (
        <ul className="users-list">
            {items.map(user => {
              const { id = "", image = "", name = "", places = "" } = user
              return <UserItem
                key={id}
                id={id}
                image={image}
                name={name}
                // placeCount={user.places.length}
                placeCount={places}
              />
           })}
        </ul>
    );
};
  • YOU ARE A GENIUS!!!!!!!! This is the solution that worked! OH MY GOODNESS GRACIOUS THANK YOU!!!!!!!!!!!!! I felt like i tried EVERYTHING- I've been combing through my files ALL DAY LONG and could not find a fix. Gone through so many Stack articles and NOTHING was working. Thank you so much!! – SJo Jan 03 '23 at 04:03