MarkFroog
MarkFroog

Reputation: 21

React js: this.state.map is not a function

I try solve my problem and can't handle that. I created API using Express JS and I want to display(map) my state array. In componentDidMount() I update listOfUsers[] and console.log return proper data. When I want map my array in render() it return some errors:

Uncaught (in promise) TypeError: this.state.listOfUsers.map is not a function

Uncaught TypeError: this.state.listOfUsers.map is not a function

Warning: Can't call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.

enter image description here

import React, { Component } from 'react';
import axios from 'axios';

class MeetingItem extends Component {
  state = {
    meetings: [],
    listOfUsers: []
  }

  componentDidMount() {
    //get meeting info
    axios.get(`http://localhost:3000/meetings/`+ this.props.match.params.id)
      .then(res => {
        const meetings = res.data;
        this.setState({ meetings });
        //console.log(res.data);
      });

    axios.get(`http://localhost:3000/takePart/`)
      .then(res => {
        for(var i = 0; i < res.data.length; i++){

          //for current meeting
          if(res.data[i]['meetingId'] == this.props.match.params.id){

            //set listOfUsers
            axios.get(`http://localhost:3000/users/`+ res.data[i]['userId'])
              .then(res => {
                const user = res.data;
                this.setState({ 
                  listOfUsers : user
                });

                //in that place data is return 
                console.log(this.state.listOfUsers);
              });
          }
        }
      });
  }

  takePart = () => {
    console.log("take");

    const takePart = {
      meetingId: this.props.match.params.id,
      userId: sessionStorage.getItem('userId'),
    };

    //x-www-form 
    let formBody = [];
    for (let property in takePart) {
        let encodedKey = encodeURIComponent(property);
        let encodedValue = encodeURIComponent(takePart[property]);
        formBody.push(encodedKey + "=" + encodedValue);
    }
    formBody = formBody.join("&");

    axios.post(`http://localhost:8000/takePart`, formBody, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } })
      .then(res => {
        console.log(res);
        console.log(res.data);

        //if successfully added a meeting then display alert
        if(res.status == "200"){
            alert("Thank you. See you later.");
        }else{
            alert("Sorry we can't handle that. Please repeat for a while.");
        }
    })
  }


  render() {
    var users = this.state.listOfUsers.map(user => {
      return (
        <p>{user.firstName}</p>
      )
    });

    return (
      <div className="MeetingItem">
        <h1>{/*this.props.match.params.id*/}</h1>
        <p>Title: {this.state.meetings['title']}</p>
        <p>Description: {this.state.meetings['description']}</p>
        <p>Author: {this.state.meetings['author']}</p>
        <p>lattitude: {this.state.meetings['lattitude']}</p>
        <p>longitude: {this.state.meetings['longitude']}</p>
        <p>date: {this.state.meetings['date']}</p>
        <p>time: {this.state.meetings['time']}</p>

        <p>{users}</p>

        <div className="btn btn-default" onClick={this.takePart}>Take part</div> 
      </div>
    );
  }
}

export default MeetingItem;

Any advice?

Upvotes: 0

Views: 10378

Answers (4)

Ilia
Ilia

Reputation: 206

Basically, the error says listOfUsers is not an array (since it doesn't have .map() method). It seems, you have an object in your response instead of an array:

axios.get(`http://localhost:3000/users/`+ res.data[i]['userId'])
          .then(res => {
            const user = res.data;
            this.setState({ 
              listOfUsers : user
            });

Upvotes: 0

FabricioG
FabricioG

Reputation: 3320

Since it is not an array you can use something like this:

let myState = Object.entries(this.state);

or Object.values();

This will allow you to iterate over each one of those. With Object.entries() for example you can loop over each key/value pair.

More info here.

Upvotes: 1

Kent V
Kent V

Reputation: 563

Inside your render() function, change from:

var users = this.state.listOfUsers.map(user => {
 return (
    <p>{user.firstName}</p>
 )
});

To:

var users = [];
if (this.state.listOfUsers) {
    user = this.state.listOfUsers.map(user => {
       return (
           <p>{user.firstName}</p>
       )
    });
}

The reason

Exception thrown is because when render() is called the first time when the after componentDidMount() hook, this.state.listOfUsers is still undefined. You need to set users = [] empty array by default and only call map() function on the array when it's become an array - after axios promise call resolved. As soon as the axios call is resolved, it well set this.state.listOfUsers to be an array and render() is called again to update UI.

Upvotes: 0

ATUL DIVEDI
ATUL DIVEDI

Reputation: 156

The API returns list of users should be an array.

To handle this error you can just put a check before map that this.state.listOfUsers is an array.

i.e.

var users = Array.isArray(this.state.listOfUsers) && this.state.listOfUsers.map(user => {
  return (
    <p>{user.firstName}</p>
  )
});

and also make sure that API axios.get('http://localhost:3000/users/'+ res.data[i]['userId']) returned the users list is an array for the expected result.

Upvotes: 2

Related Questions