Cody Lemons
Cody Lemons

Reputation: 15

TypeError: Cannot read property 'slug' of undefined

I'm not sure why my app keeps saying undefined. The error originated on line 21 of Album.js: <img id="album-cover-art" src={this.state.album.albumCover}/> I believe I need to add this.state.album somewhere, but I'm not sure where.

Index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
  , document.getElementById('root'));
registerServiceWorker();

App.js:

import React, { Component } from 'react';
import { Route, Link } from 'react-router-dom';
import './App.css';
import Landing from './components/Landing';
import Library from './components/Library';
import Album from './components/Album';

class App extends Component {
  render() {
    return (
      <div className="App">
      <header>
        <nav>
          <Link to='/'>Landing</Link>
          <Link to='/library'>Library</Link>

        </nav>
        <h1>Bloc Jams</h1>
      </header>
        <main>
          <Route exact path="/" component={Landing} />
          <Route path="/library" component={Library} />
          <Route path="/album/:slug" component={Album} />
        </main>
      </div>
    );
  }
}

export default App;

Landing.js:

import React from 'react';

const Landing = () => (
  <section className="landing">
  <h1 className="hero-title">Turn the music up!</h1>

  <section className="selling-points">
    <div className="point">
      <h2 className="point-title">Choose your music</h2>
      <p className="point-description">The world is full of music; 
why should you have to listen to music that someone else chose?</p>
    </div>
    <div className="point">
      <h2 className="point-title">Unlimited, streaming, ad-free</h2>
      <p className="point-description">No arbitrary limits. No 
distractions.</p>
    </div>
    <div className="point">
      <h2 className="point-title">Mobile enabled</h2>
      <p className="point-description">Listen to your music on the 
go. This streaming service is available on all mobile platforms.</p>
    </div>
  </section>
  </section>
);


export default Landing;

Library.js:

import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import albumData from './../data/albums';

class Library extends Component {
  constructor(props) {
    super(props);
    this.state = { albums: albumData };
  }
  render() {
    return (
      <section className='library'>
      {
        this.state.albums.map( (album, index) =>
          <Link to={`/album/${album.slug}`} key={index}>
            <img src={album.albumCover} alt={album.title} />
            <div>{album.title}</div>
            <div>{album.artist}</div>
            <div>{album.songs.length} songs</div>
          </Link>
        )
      }
      </section>
    );
  }
}


export default Library;

albums.js

export default [{
  title: 'The Colors',
  artist: 'Pablo Picasso',
  releaseInfo: '1909 Spanish Records',
  albumCover: '/assets/images/album_covers/01.jpg',
  slug: 'the-colors',
  songs: [
      { title: 'Blue', duration: '161.71', audioSrc: 
'/assets/music/blue.mp3' },
      { title: 'Green', duration: '103.96', audioSrc: 
'/assets/music/green.mp3' },
      { title: 'Red', duration: '268.45', audioSrc: 
'/assets/music/red.mp3' },
      { title: 'Pink', duration: '153.14', audioSrc: 
'/assets/music/pink.mp3' },
      { title: 'Magenta', duration: '374.22', audioSrc: 
'/assets/music/magenta.mp3' }
  ]
}, {
    title: 'The Telephone',
    artist: 'Guglielmo Marconi',
    releaseInfo: '1909 EM',
    albumCover: '/assets/images/album_covers/02.jpg',
    slug: 'the-telephone',
    songs: [
      { title: 'Blue', duration: '161.71', audioSrc: 
'/assets/music/blue.mp3' },
      { title: 'Green', duration: '103.96', audioSrc: 
'/assets/music/green.mp3' },
      { title: 'Red', duration: '268.45', audioSrc: 
'/assets/music/red.mp3' },
      { title: 'Pink', duration: '153.14', audioSrc: 
'/assets/music/pink.mp3' },
      { title: 'Magenta', duration: '374.22', audioSrc: 
'/assets/music/magenta.mp3' }
    ]
}];

Album.js:

import React, { Component } from 'react';
import albumData from './../data/albums';

class Album extends Component {
  constructor(props) {
    super(props);

    const album = albumData.find( music => {
      return album.slug === this.props.match.params.slug
    });

    this.state = {
      album: album
    };
  }

  render() {
    return (
      <section className="album">
        <section id="album-info">
        <img id="album-cover-art" src={this.state.album.albumCover}/>
        <div className="album-details">
          <h1 id="album-title">{this.state.album.title}</h1>
          <h2 className="artist">{this.state.album.artist}</h2>
         <div id="release-info">{this.state.album.releaseInfo} </div>
        </div>
      </section>
      <table id="song-list">
          <colgroup>
            <col id="song-number-column" />
            <col id="song-title-column" />
            <col id="song-duration-column" />
          </colgroup>
          <tbody>
          </tbody>
      </table>
      </section>
    );
  }
}

export default Album;

Upvotes: 1

Views: 7453

Answers (1)

Aaqib
Aaqib

Reputation: 10370

Before passing props to <Album /> you should check inside the <Route /> for matching slug i.e : albumData.find() so inside App.js before rendering <Album> :

        component={ props => {
        const album = albumData.find(music => 
        music.slug === props.match.params.slug
        );

So your Album component Route should be like :

      <Route
        exact
        path="/album/:slug" 
        component={ props => {
        const album = albumData.find(music => 
        music.slug === props.match.params.slug
        );
      return (<Album album={album} {...props}>);
      }}
      />

Make sure to move albumData inside App.js (fix the path accordingly)

import albumData from './../data/albums';

Make use of <Switch> and move your Header logic inside another Header component instead of main App.js

import React, { Component } from 'react';
import { Route, Link , Switch } from 'react-router-dom';
import './App.css';
import Landing from './components/Landing';
import Library from './components/Library';
import Album from './components/Album';
import albumData from './../data/albums';

class App extends Component {
  render() {
    return (
      <div className="App">
        <Switch>
          <Route exact path="/" component={Landing} />
          <Route path="/library" component={Library} />

          <Route
            exact
            path="/album/:slug" 
            component={ props => {
            const album = albumData.find(music => 
            music.slug === props.match.params.slug
            );

          return (<Album album={album} {...props}>);

          }}
          />
          </Switch>
      </div>
    );
  }
}

export default App;

And now inside Your Album.js you don't need to declare any constructor as it will be just passed as a props to <Album > So your Album.js should be now :

import React, { Component } from 'react';

class Album extends Component {

  render() {
    return (
      <section className="album">
        <section id="album-info">
        <img id="album-cover-art" src={this.props.album.albumCover}/>
        <div className="album-details">
          <h1 id="album-title">{this.props.album.title}</h1>
          <h2 className="artist">{this.props.album.artist}</h2>
         <div id="release-info">{this.props.album.releaseInfo} </div>
        </div>
      </section>
      <table id="song-list">
          <colgroup>
            <col id="song-number-column" />
            <col id="song-title-column" />
            <col id="song-duration-column" />
          </colgroup>
          <tbody>
          </tbody>
      </table>
      </section>
    );
  }
}
export default Album;

Or you can make use of destructing const { releaseInfo } = this.props.album and then this.props.album.releaseInfo should be just releaseInfo (You can amend this accordingly)

Upvotes: 1

Related Questions