Peter Chudinov
Peter Chudinov

Reputation: 27

React cannot read an object inside array (state)

Relatively new to React, wrote a few components before (successfully), but this is the first time I'm getting something started from scratch. I use Rails with Cassandra to provide JSON responses, and everything on the back end side is working fine.

My data this.state.data looks like this:

0 {id: {n: 2.1751612473052575e+38}, email: "[email protected]", high_score: 73, shoe_size: 10.5, updated_at: "2018-11-06T01:23:36.611-08:00"}
1 {id: {n: 2.8024982600468778e+38}, email: "[email protected]", high_score: 13, shoe_size: 7.5, updated_at: "2018-11-06T01:24:55.791-08:00"}
2 {id: {n: 2.4227336868283995e+38}, email: "[email protected]", high_score: 99, shoe_size: 10.5, updated_at: "2018-11-06T01:24:07.858-08:00"}

And doing this.state.data[1] obviously returns

{id: {n: 2.8024982600468778e+38}, email: "[email protected]", high_score: 13, shoe_size: 7.5, updated_at: "2018-11-06T01:24:55.791-08:00"}

However, this.state.data[1].email throws this

TypeError: undefined is not an object (evaluating 'this.state.data[1].email')

What can I do to access email and the rest of the data?


full component code:

import React, { Component } from 'react';

export default class Table extends Component {
  constructor() {
    super();
    this.state = {
      data: [],
    }
  }

  componentDidMount() {
    fetch('http://localhost:3000/api/users')
      .then(response => response.json())
      .then(data => this.setState({ data }))
  }

  render() {
    return(
      <div className="table">
        <h1>LOL</h1>
        <p>{this.state.data[1].email}</p>
      </div>
    )
  }
}

Upvotes: 1

Views: 683

Answers (3)

reisdev
reisdev

Reputation: 3403

The problem is that, when your component starts the rendering, the this.state.data[1].email wasn't loaded already. Just check if the data was already loaded, like below:

render(){
    if(this.state.data.length > 0)
        return(
             <div className="table">
                <h1>LOL</h1>
                <p>{this.state.data[1].email}</p>
             </div>
        )
    else return <div/>
}

The other possible solution is to use the map function. It will only render something when the array it's filled. The code is below:

render(){
    return(
         <div className="table">
            <h1>LOL</h1>
            { this.state.data.map(user => <p>{user.email}</p>) }
         </div>
    )
}

For each user in the data array, the component will render a tag <p> with the email. When there's nothing in the array, the component will render nothing.

Upvotes: 1

Hemadri Dasari
Hemadri Dasari

Reputation: 34014

This.state.data initially set to an empty array. So when your component first time renders it will be an empty array.

In componentDidMount you are making an api call and assigning api response to the state data. So this method gets called after first render

The reason you get that issue because you are accessing 1st index from this.state.data array which is obviously an empty array at the time of first render. So what you have to do is

Change

   <p>{this.state.data[1].email}</p>

To

    <p>{this.state.data.length > 0 && this.state.data[1].email}</p>

Or do iterate the array and render p elements

   {this.state.data.length > 0 && this.state.data.map(d => (  
            <p key={d.id}>d.email}</p>
   )}

If you don’t have unique id per object in array then use index as key

Upvotes: 0

Naor Tedgi
Naor Tedgi

Reputation: 5717

its a common error the request didn't complete before the render method is called just add an if statement

  render () {
    const { data } = this.state
    return (
      <div className='table'>
        <h1>LOL</h1>
        {data.length > 0 && <p>{data[1].email}</p>}
      </div>
    )
  }

Upvotes: 0

Related Questions