Jonas Alvarson
Jonas Alvarson

Reputation: 471

State is undefined when using console.log

Im getting confused.. When I try to console.log(this.state.podcast[0].collectionId) Im getting an error saying: TypeError: Cannot read property 'collectionId' of undefined.

BUT! If I remove the console.log then everything is working fine..

Im in the learning process of understanding React JS. Can someone point in the right direction?

This is my component:

import React, { Component } from 'react';
import { Link } from 'react-router-dom';

export default class Podcast extends Component {

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

  // Fetches podID from props.match
  fetchPodcast () {
    const podcastID = this.props.match.params.podID

    fetch(`https://itunes.apple.com/lookup?id=${podcastID}`)
      .then(response => response.json())
      .then(data => this.setState({
        podcast: data.results
      }));
  }

  componentDidMount () {
    this.fetchPodcast()
  }

  render() {
    console.log(this.state.podcast[0].collectionId)
    return (
      <div>
          <h2> {this.props.match.params.podID}</h2>
          <ul>
          {this.state.podcast.map(podcast =>
              <li key={podcast.collectionId}>
              <Link to={`/podcast/${podcast.collectionId}`}>{podcast.collectionName}</Link>
              </li>
          )}
          </ul>


</div>
)
  }
}

Upvotes: 0

Views: 3593

Answers (4)

Sakhi Mansoor
Sakhi Mansoor

Reputation: 8102

To see updated state, you can pass a callback to setState as second argument:

fetch(`https://itunes.apple.com/lookup?id=${podcastID}`)
  .then(response => response.json())
  .then(data => this.setState({
    podcast: data.results
  },()=>{
       console.log(this.state.podcast)
   }));

and you can conditionally render your component like this:

render() {
const { podcast} = this.state;
return (
  <div>
     {podcast.length &&
      <h2> {this.props.match.params.podID}</h2>
      <ul>
      {this.state.podcast.map(podcast =>
          <li key={podcast.collectionId}>
          <Link to={`/podcast/${podcast.collectionId}`}>{podcast.collectionName}</Link>
          </li>
      )}
      </ul>
      </h2> }
   </div>

PS. make sure you're getting data in API response and Javascript is asynchronous. You render method will be called without waiting for the API response. Once you setState anywhere your component will be re-rendered. It is showing podcast as undefined on first render

Upvotes: 0

vicke4
vicke4

Reputation: 3303

React Lifecycle methods

Above image will give you a good idea about how React works, check when the render phase happens.

So, if we relate this to your case, after constructor call render() is called, as this.state.podcast is an empty array, console.log threw error while you tried to access non existing index value. Also map function didn't execute as this.state.podcast is an empty array.

Now after componentDidMount, this.state.podcast gets value, render is called again and you get to see collection details.

Upvotes: 1

jabsatz
jabsatz

Reputation: 341

Your podcast list is empty when the render triggers. When an array is empty, Array.map will effectively run through 0 elements, which is why it doesn't throw an error. To console.log this you could first check the first element exists by using console.log(this.state.podcast[0] && this.state.podcast[0].collectionId), or simply console.log(this.state.podcast[0]) which will render the whole object if it's defined.

Upvotes: 0

Paul McLoughlin
Paul McLoughlin

Reputation: 2289

When your component first renders your this.state.podcast is empty, so your attempt to use it will show as undefined. The component re-renders when it receives an update on state and other props. So when your component initially loads, this.state.podcast is undefined, but when it receives the podcast, it re-renders again, showing the values.

Upvotes: 0

Related Questions