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!

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?

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:

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.

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!

No worries! You got this! :grinning:

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