Jammming: Search results not rendering

https://www.codecademy.com/paths/web-development/tracks/front-end-applications-with-react/modules/jammming/projects/jammming-prj

Made it to the end of the project, everything seemed to be going smoothly until I tried to use the search bar. Type in anything, press the ‘Search’ button, and nothing happens. There aren’t any errors in the console and I can’t seem to find the reason why it’s not working.

I think it could be something to do with the Spotify component:

const clientId = '*CLIENT-ID*'; <-- Removed as it shouldn't be public.
const redirectUri = 'http//localhost:3000/';
let accessToken;

const Spotify = {
  getAccessToken() {
    if(accessToken) {
      return accessToken;
    }
    
    // check for access token match
    const accessTokenMatch = window.location.href.match(/access_token=([^&]*)/);
    const expiresInMatch = window.location.href.match(/expires_in=([^&]*)/);

    if (accessTokenMatch && expiresInMatch) {
      accessToken = accessTokenMatch[1];
      const expiresIn = Number(expiresInMatch[1]);
      // This clears the parameters, allowing us to grab a new access token when it expires.
      window.setTimeout(() => accessToken = '', expiresIn * 1000);window.history.pushState('Access Token', null, '/');
      return accessToken;
    } else {
      const accessUrl = `https://accounts.spotify.com/authorize?client_id=${clientId}&response_type=token&scope=playlist-modify-public&redirect_uri=${redirectUri}`;
      window.location = accessUrl;
    }
  },

  search(term) {
    const accessToken = Spotify.getAccessToken();
    return fetch(`https://api.spotify.com/v1/search?type=track&q=${term}`, {      headers: {
        Authorization: `Bearer ${accessToken}`
      }
    }).then(response => {
      return response.json();
    }).then(jsonResponse => {
      if (!jsonResponse.tracks) {
        return [];
      }
      return jsonResponse.tracks.items.map(track => ({
        id: track.id,
        name: track.name,
        artist: track.artists[0].name,
        album: track.album.name,
        uri: track.uri
      }));
    });
  },

  savePlaylist(name, trackUris) {
    if (!name || !trackUris.length) {
      return Promise.resolve();
    };

    const accessToken = Spotify.getAccessToken();
    const headers = { Authorization: `Bearer ${accessToken}` };
    let userId;

    return fetch('https://api.spotify.com/v1/me', { headers: headers }
    ).then(response => response.json()
    ).then(jsonResponse => {
      userId = jsonResponse.id;
      return fetch(`https://api.spotify.com/v1/users/${userId}/playlists`,
      {
        headers: headers,
        method: 'POST',
        body: JSON.stringify({ name: name })
      }).then(response => response.json()
      ).then(jsonResponse => {
        const playlistId = jsonResponse.id;
        return fetch(`https://api.spotify.com/v1/users/${userId}/playlists/${playlistId}/tracks`, {
          headers: headers,
          method: 'POST',
          body: JSON.stringify({ uris: trackUris })
        })
      })
    })   
  }
}

export default Spotify;

Or maybe the App.js code:

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

import SearchBar from '../SearchBar/SearchBar';
import SearchResults from '../SearchResults/SearchResults';
import Playlist from '../Playlist/Playlist';

import Spotify from '../../util/Spotify';

class App extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      searchResults: [],
      playlistName: 'My Playlist',
      playlistTracks: []
    }
    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) {
    if (this.state.playlistTracks.find(savedTrack => savedTrack.id === track.id)) {
      return;
    }

    this.setState({playlistTracks: [this.state.playlistTracks, track]})
  }

  removeTrack(track) {
    this.setState.playlistTracks = this.state.playlistTracks.filter(currentTrack => currentTrack.id !== track.id)

    this.setState({playlistTracks: this.state.playlistTracks})
  }

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

  savePlaylist() {
    const trackUris = this.state.playlistTracks.map(track => track.uri);
    Spotify.savePlaylist(this.state.playlistName, trackUris).then(() => {
      this.setState({
        playlistName: 'New Playlist',
        playlistTracks: []
      })
    })
  }

  search(term) {
    Spotify.search(term).then(searchResults => {
      this.setState({searchResults: searchResults})
    })
  }

  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} onRemove={this.removeTrack} onNameChange={this.updatePlaylistName} onSave={this.savePlaylist} />
          </div>
        </div>
      </div>
    )
  }
}

export default App;

Otherwise it could only be one of these components:

import React from 'react';

import './SearchBar.css';

class SearchBar extends React.Component {
  constructor(props) {
    super(props);

    this.search = this.search.bind(this);
    this.handleTermChange = this.handleTermChange.bind(this);
  }

  search() {
    this.props.onSearch(this.state.term);
  }

  handleTermChange(event) {
    this.setState({term: event.target.value});
  }

  render() {
    return (
      <div className="SearchBar">
        <input placeholder="Enter A Song, Album, or Artist" onChange={this.handleTermChange} />
        <button className="SearchButton" onChange={this.handleTermChange}>SEARCH</button>
      </div>
    )
  }
}

export default SearchBar;

Search Results:

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

import './SearchResults.css';

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

export default SearchResults;

Tracklist:

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

import './TrackList.css';

class TrackList extends React.Component {
  render() {
    return (
      <div className="TrackList">
        {
          this.props.tracks && 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;

Or track:

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;

Completely lost and would really appreciate some help!

2 Likes

I also encountered the same issue. Inside the SearchBar component, what I did was to put an onClick={this.search} in the button element. You should remove the the onChange attribute in the button element.

Did it resolve the issue?

4 Likes

That did work! Search results are now coming through, thank you.

The issue now is that when I add songs to the playlist, it adds an element that’s just this: '| ', and then replaces the added song with the new added song time each time you click instead of appending it to the list.

Example:

There is probably an issue with the Tracklist component passing the props to Track component.

Specifically, there is an issue with {this.props.track}. In your Tracklist component, you wrote this code.

<div className="TrackList">
        {
          this.props.tracks && 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>

You should only be calling the map function on this.props.tracks array. I’m not sure what you are trying to achieve with the below line

this.props.tracks &&

I would remove that if I were you. That’s just my opinion. I hope it helps cuz this programming stuffs is still new to me as well. :laughing:

2 Likes

It didn’t fix the problem but I think you’re right, it didn’t need to be there.

I’ll play around with the Tracklist component and see if I can figure it out.

This whole project has been way over my head tbh!

I think I know where the mistake lie.
It’s inside your App component.

My code is this.

addTrack(track) {
    let tracks = this.state.playlistTracks;
    if (tracks.find(savedTrack => savedTrack.id === track.id)) {
      return;
    } else {
      tracks.push(track);
      this.setState({playlistTracks: tracks})
    }
  }

You missed out the tracks.push(track);
this will append the track to the array. Try it out.

1 Like

It worked! Only, now when I click ‘Save to Spotify’ it wipes the tracks but doesn’t save them to a new playlist.

It’s just bug after bug with this app :sweat_smile:

Guessing it’s something to do with ‘savePlaylist()’ in the App component or how it’s being passed to the Playlist component.

Ignore my last two replies, I didn’t realise it was supposed to wipe the saved playlist. When I checked my actual Spotify account I had a huge list of playlists named ‘My Playlist’ so it was working the whole time without me realising :joy:

Thanks for your help though Muhammad, really saved me from a few headaches!

1 Like

No worries! You got this! :grinning:

1 Like

This topic was automatically closed 18 hours after the last reply. New replies are no longer allowed.