Jason
Jason

Reputation: 409

TypeError: Cannot read property "map" of undefined

I'm following the ReactJS AJAX and APIs tutorial. I wrote a simple API in Spring, then a React component to consume that API at http://localhost:8080. The API currently returns a list of two items as follows:

[
    {brand:"Asus", make:"AAA"},
    {brand:"Acer", make:"BBB"}
]

Here's what my component looks like:

import React from 'react';
import ReactDOM from 'react-dom';

import { environment } from '../environment/environment';

export class ComputerList extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      error: null,
      isLoaded: false,
      items: [
        {brand: null, make: null}
      ]
    };
  }

  componentDidMount() {
    fetch("http://localhost:8080/computers")
    .then(res => res.json())
    .then(
      (result) => {
        // correctly displays the results
        console.log(result);
        this.setState({
          isLoaded: true,
          items: result.items
        });
      },
      (error) => {
        this.setState({
          isLoaded: true,
          error
        });
      }
    )
  }

  render() {
    const { error, isLoaded, items } = this.state;

    if(error) {
      return(<div>Error: {error.message}</div>);
    }
    else if(!isLoaded) {
      return(<div>Loading...</div>);
    }
    else if(items) {
      console.log(items);
      // error here: cannot read property "map" of undefined
      // because this.state.items is undefined somehow?
      return(
        <ul>
          {items.map(item => (
            <li key={item.make}>{item.brand} {item.make}</li>
          ))}
        </ul>
      );
    }
  }
}

At line 24, the results are successfully retrieved and logged.

But at line 54, when I try to map each result to a <li> item, the TypeError is thrown because items is somehow undefined? I've followed the answer to a similar question by initializing items at line 12, and checking items at line 48, to no avail.

How can I fix this?

Upvotes: 4

Views: 16943

Answers (3)

Hemadri Dasari
Hemadri Dasari

Reputation: 33994

JUst do this. Avoid if loops in render instead use && operator or ternary operator directly in return to manage if checks

  return(
    <ul>
      {!isLoaded && <div>Loading...</div>}
       {error && !isLoaded && <div>Error: {error.message}</div>)}
      {!isLoaded && items && items.map(item => (
        <li key={item.make}>{item.brand} {item.make}</li>
      ))}
    </ul>
  );

Also your make key in object initially contains null value hence you cannot assign that as a key. Instead what you can do is try to generate unique id for object in your array from the backend or else use something like below

 return(
    <ul>
      {items && items.map((item, index) => (
        <li key={"key"+index}>{item.brand} {item.make}</li>
      ))}
    </ul>
  );

Excuse me for typo issues because I am answering from my mobile

Upvotes: 0

Jason
Jason

Reputation: 409

Thanks to @DacreDenny for the advice :)

Line 27: items: result.items. This line expects the response to contain an Object named "items".

But my API only returns an Array of objects. So I changed the line to

Line 27 to: items: result. This saves the entire array to state.items. Then it can be properly mapped and rendered.

Upvotes: 1

Dacre Denny
Dacre Denny

Reputation: 30360

This is likely due to the type of items being something other than an array, which is why the map() method would not be defined.

For a more robust render() method, you can replace else if(items) { with else if(Array.isArray(items)) {, which should protect against the error message you're seeing:

render() {
    const { error, isLoaded, items } = this.state;

    if(error) {
      return(<div>Error: {error.message}</div>);
    }
    else if(!isLoaded) {
      return(<div>Loading...</div>);
    }
    else if(Array.isArray(items)) { // <-- update here
      console.log(items);
      // error here: cannot read property "map" of undefined
      // because this.state.items is undefined somehow?
      return(
        <ul>
          {items.map(item => (
            <li key={item.make}>{item.brand} {item.make}</li>
          ))}
        </ul>
      );
    }
  }

Hope that helps!

Upvotes: 2

Related Questions