How to display data from api in React

Viewed 90

I am new to React. My application uses Laravel and React.

When trying to display data received from an api call, I am getting the following error:

TypeError: this.state.userlist.map is not a function

The api call was successful and I am able to view the data from the response in the console (See screenshot below).

View.js

import React from 'react';
import axios from 'axios';

export class View extends React.Component{

constructor(props){
super(props);
this.state = {
  userlist: []
};
this.usersList = this.usersList.bind(this);
}

componentDidMount(){
axios.get('http://127.0.0.1:8000/api/test')
.then(res => {
  console.log(res.data);
  this.setState({ userlist: res.data });
})
.catch(error => console.log(error));
}

usersList(){
  this.state.userlist.map( (val,i) => {
    return(
  <tr>
    <td>{val.fname}</td>
    <td>{val.lname}</td>
    <td>{val.email}</td>
    <td>{val.phone}</td>
  </tr>
 )
})
}

render(){
return(
  <table>
    <thead>
      <tr>
        <th>First Name</th>
        <th>Last Name</th>
        <th>Email</th>
        <th>Phone</th>
      </tr>
    </thead>
    <tbody>

    { this.usersList() }

    </tbody>
  </table>
)
}
}

Screenshot of error and response in the console

error image

6 Answers

You have to put res.data.data to setState

Example: this.setState({ userlist: res.data.data });

an Array with data you need is inside res.data

Please find a working example with dummy data for illustrative purposes.

import React from "react";

const users = [
  { fname: "J", lname: "Smith", email: "e@j.com", phone: "12345" },
  { fname: "S", lname: "Joe", email: "b@j.com", phone: "6789" }
];

export default class View extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      userlist: []
    };
  }

  componentDidMount() {
    this.setState({ userlist: users });
  }

  render() {
    return (
      <table>
        <thead>
          <tr>
            <th>First Name</th>
            <th>Last Name</th>
            <th>Email</th>
            <th>Phone</th>
          </tr>
        </thead>
        <tbody>
          {this.state.userlist.map((val, i) => {
            return (
              <tr>
                <td>{val.fname}</td>
                <td>{val.lname}</td>
                <td>{val.email}</td>
                <td>{val.phone}</td>
              </tr>
            );
          })}
        </tbody>
      </table>
    );
  }
}

 Working Example

I got my answer. But I want to map rows in different function and then want to call that function within table. As I did in my posted question.

import React from 'react';
import axios from 'axios';

export class View extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      userlist: [],
    };
  }

  componentDidMount() {
    axios.get('http://127.0.0.1:8000/api/test')
      .then(res => {
        console.log(res.data);
        this.setState({ userlist: res.data.data });
      })
      .catch(error => console.log(error));
  }

  render() {
    const { userlist } = this.state;
    return (
      <table>
        <thead>
          <tr>
            <th>First Name</th>
            <th>Last Name</th>
            <th>Email</th>
            <th>Phone</th>
          </tr>
        </thead>
        <tbody>

          {userlist.length > 0 ?
            userlist.map((val, index) => {
              return <tr key={index}>
                <td>{val.fname}</td>
                <td>{val.lname}</td>
                <td>{val.email}</td>
                <td>{val.phone}</td>
              </tr>;
            }) : null
          }
        </tbody>
      </table>
    );
  }
}

here is the solution

import React from 'react';
import axios from 'axios';

export class View extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      userlist: [],
    };
  }

  componentDidMount() {
    axios.get('http://127.0.0.1:8000/api/test')
      .then(res => {
        console.log(res.data);
        this.setState({ userlist: res.data });
      })
      .catch(error => console.log(error));
  }

  render() {
    const { userlist } = this.state;
    return (
      <table>
        <thead>
          <tr>
            <th>First Name</th>
            <th>Last Name</th>
            <th>Email</th>
            <th>Phone</th>
          </tr>
        </thead>
        <tbody>

          {userlist.length > 0 ?
            userlist.map((val, index) => {
              return <tr key={index}>
                <td>{val.fname}</td>
                <td>{val.lname}</td>
                <td>{val.email}</td>
                <td>{val.phone}</td>
              </tr>;
            }) : null
          }
        </tbody>
      </table>
    );
  }
}

I have also encountered same issue several times before, the one and only solution that worked for me was to replace {} with (). i.e.

{userlist.length > 0 ?
            userlist.map((val, index) => (
              return <tr key={index}>
                <td>{val.fname}</td>
                <td>{val.lname}</td>
                <td>{val.email}</td>
                <td>{val.phone}</td>
              </tr>;
            )) : null
          }

Notice the use of ( instead of { after userlist.map((val, index) => . This should fix your error.

You can use Short-Circuit operator (&&) to conditionally check if the data exists and then fetch it like this

usersList(){
  this.state.userlist && this.state.userlist.map( (val,i) => {
    return(
  <tr>
    <td>{val.fname}</td>
    <td>{val.lname}</td>
    <td>{val.email}</td>
    <td>{val.phone}</td>
  </tr>
 )
})
}

The reason this is required because the data fetching process from API is asynchronous and it might take some time and before that, your program reaches usersList() and throws that error.

So here, you are conditionally checking first using && and if data exists from API then you call this logic.

Related