Jammming (Cannot read property 'map' of undefined)


I’m not the best with React & JSX, but please can someone help me understand how ‘tracks’ is undefined here?

Code for TrackList.js and Track.js are below. I ought to let you know that ‘track’ reads as undefined too. Thank you.

TrackList.js

import React from ‘react’;
import ‘./TrackList.css’;
import Track from ‘…/Track/Track’;

class TrackList extends React.Component {
render() {
return(

{this.props.tracks.map(track => {return
},

)

}
</div>
)

};

};

export default TrackList;

Track.js

import React from ‘react’;
import ‘./Track.css’;

class Track extends React.Component {
constructor(props){
super(props);
this.addTrack = this.addTrack.bind(this);
this.removeTrack =this.removeTrack.bind(this);
}

renderAction() {
if(this.props.isRemoval) {
return -
} else {
return +
}

}

addTrack() {
this.props.onAdd(this.props.track);
}

removeTrack() {
this.props.onRemove(this.props.track);
}

render() {
	return(

{this.props.track.name}

{this.props.track.artist} | {this.props.track.album}

{this.renderAction()}
)
}

};

export default Track;

When you ask a question, don’t forget to include a link to the exercise or project you’re dealing with!

If you want to have the best chances of getting a useful answer quickly, make sure you follow our guidelines about how to ask a good question. That way you’ll be helping everyone – helping people to answer your question and helping others who are stuck to find the question and answer! :slight_smile:

First of all, please take a minute to take a brief look at this post about how to write code in the forums. Trust me, you do that and it becomes easier to read for everyone, hence being more likely that you’ll get a response.

About your question, yes, I suspect I know the culprit (because I was stuck in the same situation not too long ago :slight_smile: ). I will try and give you an idea that hopefully will help: since <Track \> is instantiated by every <TrackList \>, have a look around every file, to see if you find any other <TrackList \> instantiated but that doesn’t pass the props (the tracks).

1 Like

Thanks, dude. Every other that is there does pass tracks as props.

1 Like

Every other *

I am so stuck I can’t lie, so might just re-post this with the formatting requirements that post you sent showed me

1 Like

Don’t worry, most of the time being stuck is frustrating, true, but also a good sign that you’re learning and getting into more advanced stuff. :muscle:

Do you think you could screenshot the React Dev Tools output? Maybe that would help debug this.

Also, do you think you could post again the code you pasted above, but this time in the way explained here? That would help a lot to read it more clearly.

NOTE: I noticed a couple of things in the screenshot you sent, but I am not sure they are related to the problem itself. So, if you want, let’s tackle it from the start by looking at both of those things mentioned first.

Sounds reasonable?

Yeah sounds good mate. Thank you for this, I appreciate it! So to start with, Track.js and TrackList.js formatted below:

Track.js

import React from 'react';
import './Track.css';

class Track extends React.Component {
	constructor(props){
		super(props);
		this.addTrack = this.addTrack.bind(this);
		this.removeTrack =this.removeTrack.bind(this);
	}

renderAction() {
	if(this.props.isRemoval) {
		return <button className="Track-action" onClick={this.removeTrack}>-</button>
	} else {
       return <button className="Track-action" onClick={this.addTrack}>+</button>
	}

}

addTrack() {
	this.props.onAdd(this.props.track); 
}

removeTrack() {
	this.props.onRemove(this.props.track);
}

	render() {
		return(
<div className="Track">
  <div className="Track-information">
    <h3>{this.props.track.name}</h3>
    <p>{this.props.track.artist} | {this.props.track.album}</p>
  </div>
  {this.renderAction()}
</div>

	)
	}
};


export default Track;

TrackList.js

import React from 'react';
import './TrackList.css';
import Track from '../Track/Track';



class TrackList extends React.Component {
	render() {
		return(
<div className="TrackList"> {
	this.props.tracks.map(track => {
		return (
			<Track track={track} key={track.id} 
    	onRemove={this.props.onRemove} isRemoval={true} />
    	)

    }

    )

	}
	</div>
	)

	};
};
	




export default TrackList;

And here is a screenshot of the React Dev Tools output:

Thank you,

Ben

First of all, awesome job with posting the code and the Dev Tools output in a clear way, Ben! That’s really helpful info.

Plus now the return with your <Track /> component is properly wrapped in parentheses (not always necessary, only on multiple lines, but let’s say just to be sure).

Now, I will still tell you again something I told you earlier, but this time let me quote it first and then rephrase it, or rather explain it in a more step-by-step way…

Right now, we are mainly looking at two files, namely Tracklist.js and Track.js . But I am sure you have more files in your project (because I did on mine too :smiley: ) .

Now, let’s analyze this, what’s the compiler telling us? It’s telling us:

TypeError: Cannot read property 'map' of undefined

…and it’s also telling us also where this happens:

at TrackList.render (TrackList.js:10) // (Note: The 10 means it's on line 10 of that file)

…which is, obviously where you call map(), in other words, here:

this.props.tracks.map(track => {

But did you see what’s telling us? That whatever is calling map() is undefined. The question then is What’s calling map() here . Well, this.props.tracks, right?

That’s puzzling! If this.props.tracks is undefined, then you must not be passing the props down, but I suspect you are passing them here, right? There must be any this.props.tracks that is undefined, but where?

Well, let’s do this, let’s see where do you pass down the props.tracks, okay? But how do we know that? There are probably different ways, but one of them would be to use the CTRL + F functionality (in your code editor or inside the Codecademy’s files in the project (although I think in this project, you do it on your own code editor, right?). Now, with patience, you can now go through each one of the files, and make CTRL+ F searching for the text <TrackList (important not to miss the opening < since we are looking for instantiation, not just use of it, and to only search until the end of the TrackList, nothing else). I know this may be a little tedious way, but since your compiler wont go into React Dev Tools, it’s a way I came up with, if you find a better way to do this, definitely go ahead!).

The important goal of doing this is identifying where do you instantiate <TrackList />. Now, if you make this search in all files of the project, you may find more than one hit (I know I did). In that case, you will have to look that each and every one of these <TrackList instances is actually receiving (or being passed down) the tracks props, as in it can’t be just <TrackList /> on any file, it has to be <TrackList tracks={...bla bla} ... /> .

I am sorry if this is lengthy, but I am 99% sure this should help you spot the issue and fix it :wink: which is our goal (wait, if this still doesn’t solve it, follow up here and let’s tackle it). And if all goes well, this should also help you debug mysterious issues in the future, too! How cool is that?

Let me know if you give it a go and how it goes!

Thanks mate. The only other <TrackList component I can find though, is in SearchResults.js, which does pass down props.tracks. I’ll paste the syntax for SearchResults.js below, but is there something else wrong with that <TrackList? Or should I be passing props.tracks to the <TrackList component in another file? If that could be the case, then I’ll paste the rest of my code here. But for now, here’s SearchResults.js below:

SearchResults.js

import React from 'react';
import './SearchResults.css';
import TrackList from '../TrackList/TrackList';

class SearchResults extends React.Component {
	render() {
		return (
<div className="SearchResults">
  <h2>Results</h2>
  <TrackList tracks={this.props.tracks} track={this.props.SearchResults} onAdd={this.props.onAdd} isRemoval={false} />
</div>
		)
	}
};



export default SearchResults;

Cheers,

Ben

Cool! By the way, great effort, that code is so clear to read now. :tada:

Now let’s take this chance to team-debug this. :slight_smile:

OK, I now see you are passing down the props.tracks to this TrackList. Forgive me please if you told me and I forgot, but question 1) did you also find the <TrackList /> in Playlist.js? Did you see that one is also passing down props.tracks?

If the answer is yes, let’s see the next thing…

Question 2) Can we see the file(s) in/through which you pass this props (tracks and SearchResults according to your output above) down to this SearchResults component? I mean this to see if the chain of props could be broken somewhere, etc. It’s likely this would be the App.js file.

Question 3) Can we also see the same in reference to the Playlist.js file? :smiley: Sorry, I know we are asking you to post a lot of code, but it’s good practice anyway, right? So, I think the chain of Tracklist-Playlist-and-above, goes through, obviously, Playlist file itself, plus App.js (by the way, I followed the same method I told you earlier about searching <Playlist .

Looking forward to your response there. Let’s do this!

EDIT NOTE: just don’t post the Spotify.js file, or if you do, make sure to change the client ID (for your own security).

Hey,

1) Sorry for the late response, was on holiday! First of all, there is no <TrackList /> in Playlist.js, I believe I took that one out a while back, for a reason I don’t remember now :man_facepalming: - could this at least be part of the problem? I’ve pasted Playlist.js at the bottom of this comment.

2) Yes, it is just App.js that passes down said props to the SearchResults component. I’ll paste that below too.

3) I appreciate your help by the way mate. App.js and Playlist.js pasted below.

App.js

import React from 'react';
import './App.css';
import SearchBar from '../SearchBar/SearchBar';
import SearchResults from '../SearchResults/SearchResults';
import Playlist from '../Playlist/Playlist';

class App extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      SearchResults: [{ name: 'name1', artist: 'artist1', album: 'album1', id: 1}, 
      {name: 'name2', artist: 'artist2', album: 'album2', id: 2}, {name: 'name3', artist: 'artist3', album: 'album3', id: 3}],
      playlistName: 'Ma 15th bday',
      playlistTracks: [{ name: 'playlistName1', artist: 'playlistArtist1', album: 'playlistAlbum1', id: 4}, {name: 'playlistName2', artist: 'playlistArtist2', album: 'playlistAlbum2', id: 5}, {name: 'playlistName3', artist: 'playlistArtist3', album: 'playlistAlbum3', id: 6}]  
    }
    this.addTrack = this.addTrack.bind(this);
    this.removeTrack = this.removeTrack.bind(this);
    this.updatePlaylistName = this.updatePlaylistName.bind(this);
    this.savePlaylist = this.savePlaylist.bind(this);
    this.search = this.search.bind(this);
  }
addTrack(track){
  let tracks = this.state.playlistTracks;
  if (tracks.find(savedTrack => savedTrack.id === track.id)) {
    return;
  }

  tracks.push(track);
  this.setState({ playlistTracks: tracks });
}

removeTrack(track){
  let tracks = this.state.playlistTracks;
  tracks = tracks.filter(currentTrack => currentTrack.id !== track.id) 
 this.setState({ playlistTracks: tracks });
  }

  updatePlaylistName(name){
    this.setState({ playlistName: name });
  }

  savePlaylist(){
    let trackUris = this.state.playlistTracks.map(track => track.uri); 
  }

  search(term){
  console.log(term);
  
  }

render() {
    return (
    <div>
  <h1>Ja<span className="highlight">mmm</span>ing</h1>
  <div className="App">
    <SearchBar onSearch={this.search} />
    <div className="App-playlist">
      <SearchResults searchResults={this.state.searchResults} onAdd={this.addTrack}
       />
      <Playlist playlistName={this.state.playlistName} playlistTracks={this.state.playlistTracks} onNameChange={this.state.updatePlaylistName} onSave={this.savePlaylist} />
    </div>
  </div>
</div>
    )
  }
}


export default App;

Playlist.js

import React from 'react';
import './Playlist.css';
import TrackList from '../TrackList/TrackList';


class Playlist extends React.Component {
constructor(props){
super(props); 
this.handleNameChange = this.handleNameChange.bind(this);

};

handleNameChange(event) {
	this.props.onNameChange(event.target.value);

}
	render() {
		return(

<div className="Playlist">
  <input defaultValue={"New Playlist"} onChange={this.handleNameChange} />
  <button className="Playlist-save" onClick={this.props.onSave}>SAVE TO SPOTIFY</button>
</div>


)
	}
};




export default Playlist;