I am getting "TypeError: undefined is not an object (evaluating 'this.props.tracks.map')" in the browser, although app compiles successfully.
I checked with console.log that both SearchResults and TrackList receive props (console.log prints all elements of array within map() method) so I do not understand why the object is undefined.
Code is presented below:
File App.js
import React from 'react';
import SearchResults from '../SearchResults/SearchResults.js'
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
searchResults: [{name: '1'}, {name: '2'}]
}
}
render() {
return (
<div>
<div class="App">
<div className="App-playlist">
<SearchResults searchResults={this.state.searchResults} />
</div>
</div>
</div>
)
}
}
export default App;
File SearchResults.js
import React from 'react';
import TrackList from '../TrackList/TrackList.js'
class SearchResults extends React.Component {
render() {
return (
<div className="SearchResults">
<TrackList tracks={this.props.searchResults}/>
</div>
)
}
}
export default SearchResults;
File TrackList.js
import React from 'react';
import Track from '../Track/Track.js'
class TrackList extends React.Component {
render() {
return (
<div className="TrackList">
{
this.props.tracks.map(track => {
return <Track track={track}/>
})
}
</div>
)
}
}
export default TrackList;
Update
I also added error from the browser:
Not exactly sure why this is happening, but when console logging this.props.tracks in Tracklist it is the defined, passed array from SearchResults then becomes undefined, thus causing the error you see.
QUICK FIX Use Guard Pattern
class TrackList extends React.Component {
render() {
const { tracks } = this.props;
console.log(tracks); // two logs, first (2)[{...}, {...}], then undefined
return (
<div className="TrackList">
{
tracks && tracks.map((track, index) => { // Use a guard here to handle undefined tracks prop
return <Track key={index} track={track} />
})
}
</div>
)
}
}
In the above snippet you can simply place a guard on the tracks prop to defend against undefined values for the mapping. The more important question though is why this is occurring since the prop is never undefined here in SearchResult:
class SearchResults extends React.Component {
render() {
console.log('SearchResults', this.props.searchResults); // defined, only see 1 log entry
return (
<div className="SearchResults">
<h2>Results</h2>
<TrackList tracks={this.props.searchResults} />
</div>
)
}
}
FURTHER INVESTIGATION
I looked over the rest of your code and found that App is rendering PlayList, and PlayList also renders a TrackList but this time passes no tracks!
class Playlist extends React.Component {
render() {
return (
<div className="Playlist">
<input defaultValue={'New Playlist'} />
<TrackList /> // tracks becomes undefined here
<button className="Playlist-save">SAVE TO SPOTIFY</button>
</div>
)
}
}
SOLUTION Define Props/DefaultProps
At this point I would recommend using the react prop-types package to define your component's props and provide default values in some cases. Your TrackList component becomes:
import React from 'react';
import PropTypes from 'prop-types'; // import proptypes
import Track from '../Track/Track.js'
import './TrackList.css'
const propTypes = {
tracks: PropTypes.array // If you make this required (i.e. PropTypes.array.isRequired) then you'll get a react warning about missing props
};
const defaultProps = {
tracks: [], // if tracks props is unspecified this value is used
};
class TrackList extends React.Component {
render() {
const { tracks } = this.props;
console.log(tracks);
return (
<div className="TrackList">
{
tracks.map((track, index) => { // no longer need guard
return <Track key={index} track={track} />
})
}
</div>
)
}
}
TrackList.propTypes = propTypes;
TrackList.defaultProps = defaultProps;
export default TrackList;
Related
I was trying to make a playlist project with react using Spotify developer tools and Api. Inside TrackList.js I am getting a TypeERROR "Cannot read property 'map' of undefined. Can you help me understand there the error?
import React from 'react';
import './TrackList.css';
import Track from '../Track/Track';
class TrackList extends React.Component {
render() {
return (
<div class="TrackList" >
{
this.props.tracks.map(track => {
return <Track track={track}
key={track.id}
onAdd={this.props.onAdd}
onRemove={this.props.onRemove}
isRemoval={this.props.isRemoval} />
})
}
</div>
)
}
}
export default TrackList;
The value of
this.props.tracks
might not be the array, try this following code to render component only when value is array.
import React from "react";
import "./TrackList.css";
import Track from "../Track/Track";
class TrackList extends React.Component {
render() {
const { tracks } = this.props;
return (
<div class="TrackList">
{Array.isArray(tracks) &&
tracks.map(track => {
return (
<Track
track={track}
key={track.id}
onAdd={this.props.onAdd}
onRemove={this.props.onRemove}
isRemoval={this.props.isRemoval}
/>
);
})}
</div>
);
}
}
export default TrackList;
check your tracks array
import React from "react";
import "./TrackList.css";
import Track from "../Track/Track";
class TrackList extends React.Component {
render() {
const { tracks } = this.props;
return (
<div class="TrackList">
{Array.isArray(tracks) &&
tracks.map(track => {
return (
<Track
track={track}
key={track.id}
onAdd={this.props.onAdd}
onRemove={this.props.onRemove}
isRemoval={this.props.isRemoval}
/>
);
})}
</div>
);
}
}
In my app i have an initial state in a component App.js it's an array of objects
Here is App.js code:
import React, { Component } from 'react';
import './App.css';
import { render } from '#testing-library/react';
// Import Used Components
import SearchBar from '../SearchBar/SearchBar';
import Playlist from '../PlayList/PlayList';
import SearchResults from '../SearchResults/SearchResults';
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}]
};
}
// Adding JSX to App Component
render() {
return (
<div>
<h1>Ja<span className="highlight">mmm</span>ing</h1>
<div className="App">
<SearchBar />
<div className="App-playlist">
<SearchResults searchResults={this.state.searchResults} />
<Playlist />
</div>
</div>
</div>
);
}
}
export default App;
I passed this initial state as a prop called searchResults to another component named .
Here is searchResults.js code :
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.searchResults}/>
</div>
);
}
}
export default SearchResults;
then I used passed this prop to another component called TrackList
here is TrackList.js code:
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} />;
} )
}
</div>
);
}
}
export default TrackList;
In Track.js I want to map through this initial state array to render a component called Track
here is the Track.js code:
import React from 'react';
import './Track.css';
class Track extends React.Component {
renderAction() {
if (this.props.isRemoval){
return <botton className='Track-action'>-</botton>;
} else {
return <botton className='Track-action'>+</botton>;
}
};
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>
<button className="Track-action">{this.renderAction}</button>
</div>
);
}
}
export default Track;
But something is wrong !! I keep getting this error:
TypeError: Cannot read property 'map' of undefined
Here is searchBar.js component code:
import React from 'react';
import './SearchBar.css';
class SearchBar extends React.Component {
render() {
return (
<div className="SearchBar">
<input placeholder="Enter A Song, Album, or Artist" />
<button className="SearchButton">SEARCH</button>
</div>
);
}
}
export default SearchBar;
HERE LINK TO THE PROJECT WITH THE SAME ERROR ON SANDBOX
https://codesandbox.io/s/upbeat-dawn-lwbxb?fontsize=14&hidenavigation=1&theme=dark
Change your TrackList component to this:
class TrackList extends React.Component {
render() {
return (
<div className="TrackList">
{this.props.tracks && this.props.tracks.map(track => {
return <Track key={track.id} track={track}/>
})}
</div>
);
}
}
You can't map through this.props.tracks if it is undefined.
The && (AND operator) is a concise way to conditionally render in React. You can think of it like a simple if statement: If the expression on the left is true, then do x.
I'll also expand on why the this.props.tracks was undefined in a certain instance in your case.
The reason that this problem is happening is your Playlist component. If you uncomment this component from your App you will notice your original code will work.
This is because your PlayList component, like your SearchResults component, also renders your TrackList component. The problem is you haven't passed your state and props down to TrackList like you did with your SearchResults component.
So an alternative solution would be to pass your state and props down from PlayList to TrackList:
App.js
// ...
<SearchResults searchResults={this.state.searchResults} />
<Playlist searchResults={this.state.searchResults}/>
// ...
PlayList.js
// ...
<TrackList tracks={this.props.searchResults}/>
// ...
I am not sure why I am getting TypeError: Cannot read property 'map' of undefined here:
I am trying to pass an array down from App.js to => SearchResult.js to => TrackList.js
TrackList.js:
import React from 'react';
import Track from '../Track/Track'
import '../TrackList/TrackList.css';
class TrackList extends React.Component {
render() {
return (
<div className="TrackList">
{this.props.tracks.map(track => {
console.log(track);
<Track track={track} />}
)}
</div>
);
}
}
export default TrackList;
the console.log(track) above returns an array of objects as expected, I think that makes sure that this.props.tracks is an array.
App.js:
import React, { Component } from 'react';
import './App.css';
import SearchBar from './Components/SearchBar/SearchBar';
import SearchResults from './Components/SearchResults/SearchResults';
import Playlist from './Components/Playlist/Playlist';
const track = {
name: "Tiny Dancer",
artist: 'Elton John',
album: 'Madman Across The Water'
};
const tracks = [track, track, track];
class App extends Component {
constructor (props) {
super(props);
this.state = {'searchResults': tracks};
}
render() {
return (
<div>
<h1>Ja<span className="highlight">mmm</span>ing</h1>
<div className="App">
<SearchBar />
<div className="App-playlist">
<SearchResults searchResults={this.state.searchResults}/>
<Playlist />
</div>
</div>
</div>
);
}
}
export default App;
SearchResults.js:
import React from 'react';
import '../SearchResults/SearchResults.css';
import TrackList from '../TrackList/TrackList';
class SearchResults extends React.Component {
render() {
return (
<div className="SearchResults">
{/* {console.log(this.props.searchResults)} */}
<h2>Results</h2>
<TrackList tracks={this.props.searchResults}/>
</div>
);
}
}
export default SearchResults;
I also tried using props to pass the array from App.js (instead of state) but I got the same error.
sometimes react will render the app before your props loaded. this especially happens if you're getting props form an api call. react will throw an error if that happens. here's a work around:
import React from 'react';
import Track from '../Track/Track'
import '../TrackList/TrackList.css';
class TrackList extends React.Component {
constructor(props){
super(props)
this.state={}
}
render() {
//es6 syntax. this will allow the app to render and then replace the
//tracks variable with this.props.tracks when the props are ready
//this check if this.props.tracks exist and if it doesn't it will
//assign the variable to an empty array. then .map will not be
//called on an undefined variable.
let tracks = this.props.tracks ? this.props.tracks : [];
return (
<div className="TrackList">
{tracks.map(track => {
console.log(track);
<Track track={track} />}
)}
</div>
);
}
}
export default TrackList;
I have the following two classes. I think the main problem here is that the render function passes the initial states instead of the updated states that are updated with the help of the YTsearch API. If I print the information about videos on console, I do receive the relevant information about the searched query in terms of an Object. But when passing these objects to a new Component (Title) it seems to be undefined (null).
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Component } from 'react';
import Youtube from './Youtube';
import Title from './Title';
import YTSearch from 'youtube-api-search';
const key = '************************************';
class YoutubeVideo extends React.Component {
constructor(props) {
super(props);
this.state = {video:'', selectedVideo:'', received: false};
this.getvideos();
}
getvideos() {
YTSearch({key: key, term: 'football'}, (videos) => {
this.setState({
videos: videos,
selectedVideo: videos[0],
received : true
});
});
}
render() {
if (this.state.received){
return (
<Title videoTitle={ this.state.selectedVideo }/>
)
}
return (
<div>
</div>
)
}
}
ReactDOM.render(
<YoutubeVideo />,
document.getElementById('root')
);
Title.js
import React, { Component } from 'react';
class Title extends Component {
render () {
const video = this.props.videoTitle;
return (
<div>
<div>{ this.video.snippet }</div>
<div>{ this.video.snippet }</div>
</div>
)
}
}
export default Title;
I get the following error in the Title.js:
TypeError: Cannot read property 'snippet' of undefined
Please help.
In Title.js, use {video.snippet} not this.video.snippet.
you can use react component life cycle methods to call getvideos() method.
I'm having a problem with the function fetch. I'm trying to send just a number for example "1", and I have access to this data in all child components, but after calling fetch, I'm no longer able to access this data.
App.jsx:
import React, { Component } from 'react'
import './App.css';
import fetch from 'isomorphic-fetch'
import Header from './Header'
import Content from './Content'
import Footer from './Footer'
class App extends Component {
constructor(props) {
super(props)
this.state = {
stripdata: null
}
}
componentWillMount() {
fetch(`http://localhost:3000/data/info.json`)
.then(results => results.json())
.then(data => {
this.setState({
stripdata: data
})
// console.log(this.state.stripdata)
})
.catch(err => {
console.log("Didn't connect to API", err)
})
}
render() {
// console.log(this.state.stripdata)
return (
<div className="App">
<Header onQuery={1}/>
{
(this.state.data === null) ? <div className="loading">Loading data...</div> : <Content onResult={this.state.stripdata}/>
}
<Footer />
</div>
);
}
}
export default App;
Content.jsx:
import React, { Component } from 'react'
import Result from './Result'
class Content extends Component {
constructor(props) {
super(props);
this.state = {
stripdata: this.props.onResult
};
}
componentWillMount() {
}
render() {
console.log("im an Content: " + this.state.stripdata)
return (
<div className="Content">
<Result stripdata={ this.state.stripdata }/>
</div>
);
}
}
export default Content;
Result.jsx:
import React, { Component } from 'react'
import PersonCard from './PersonCard'
class Result extends Component {
constructor(props) {
super(props);
this.state = {
stripdata: this.props.stripdata
};
}
componentWillMount() {
}
render() {
console.log("im the Result: " + this.state.stripdata)
return (
<div className="result">
<PersonCard />
</div>
);
}
}
export default Result;
Please help. This is blocking my progress.
Fix the issue here:
<Header onQuery={1}/>
{
(this.state.stripdata === null) ? <div className="loading">Loading data...</div> : <Content onResult={this.state.stripdata}/>
}
You need to check properties in state with name stripdata.
And btw, fetch has to be performed in ComponentDidMount, see https://daveceddia.com/where-fetch-data-componentwillmount-vs-componentdidmount/
The problem is that, in your Results, you are only using the value from props once: in the constructor, where you set to state.
You should not set value in state from props. Instead, just use the props directly. Change Result to as following, then it will work proper:
import React, { Component } from 'react'
import PersonCard from './PersonCard'
class Result extends Component {
constructor(props) {
super(props);
// removed setting state from props.stripdata
}
render() {
console.log("im the Result: " + this.props.stripdata) // <-- using props!
return (
<div className="result">
<PersonCard />
</div>
);
}
}
export default Result;
In general it is considered bad practice/antipattern to set state from props.