Jamming Project - Search Continues to not work

Project link: Jammming

Howdy!

So I have been working on the Jammming project, following the help video step-by-step. However, after I completed step 95 (basically coping the video tutorial), if I put in a term query in my search box, nothing happens. I’ve been trying to troubleshoot this, and I can not figure out what went wrong. I don’t even get an error thrown, so I’m having difficulty finding where I even went wrong. Any help would be great, thank you!

I have copied my code below for the related pages, but to view the full code you can visit: https://github.com/persigio/jamming

App.js:

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 = {
      playlistName: 'Playlist 1',
      playListTracks: [],
      searchResults: []
    }

    this.addTrack = this.addTrack.bind(this);
    this.removeTrack = this.removeTrack.bind(this);
    this.savePlaylist = this.savePlaylist.bind(this);
    this.search = this.search.bind(this);
    this.updatePlaylistName = this.updatePlaylistName.bind(this);
  }
  
  addTrack(track){
    const addList = this.state.playListTracks;
    if(addList.find(saved => saved.id === track.id)){
      return;
    }
    addList.push(track);
    this.setState({playListTracks: addList});
  }

  removeTrack(track){
    const removeList = this.state.playListTracks.filter(current => current.id !== track.id);
    this.setState({playListTracks: removeList});
  }

  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(result =>{
      console.log(result);
      this.setState({searchResults: result});
    });
    
  }

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

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

}

export default App;

Spotify.js:

import spotifyClientID from './privateInfo';

const clientID = spotifyClientID;
const redirectURI = 'http://localhost:3000/';

let accessToken ;
const Spotify = {
    getAccessToken(){
        if(accessToken ){
            return accessToken ;
        }

        //check for an access token match
        const accessTokenMatch= window.location.href.match(/access_token=([^&]*)/);
        const expiresInmatch = window.location.href.match(/expires_in=([^&]*)/);

        //if token and expires exists in url 
        if(accessTokenMatch && expiresInmatch){
            accessToken  = accessTokenMatch[1];
            let expiresIn = Number(expiresInmatch[1]);

            // This clears the parameters, allowing us to grab a new access token when it expries.
            window.setTimeout(()=> accessToken  = '', expiresIn *1000);
            window.history.pushState('Access Token', null,'/');
            return accessToken;
        }else{
            window.location = `https://accounts.spotify.com/authorize?client_id=${clientID}&response_type=token&scope=playlist-modify-public&redirect_uri=${redirectURI}`;
        }
    },

    search(term){
        const accessToken = Spotify.getAccessToken();
        const searchLink =`https://api.spotify.com/v1/search?type=track&q=T${term}`;

        return fetch(searchLink, { headers:{Authorization: `Bearer ${accessToken}`} }
        ).then(response => {
            response.json();
            }
        ).then(jsonResponse=>{
            if(!jsonResponse.tracks){
                return [];
            }
            return jsonResponse.tracks.items.map(track => ({
                id: track.id,
                name: track.name,
                artist: track.artist[0].name,
                album: track.album.name,
                uri: track.uri
            }));
        });
    },

    savePlaylist(name, trackURIs){
        if(!name || !trackURIs.length){
            return;
        }

        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`,{
                method: 'POST',
                headers: headers,
                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`), {
                    method: 'POST',
                    headers: headers,
                    body: JSON.stringify({uris: trackURIs})
                }
                }
            )
            }
        )
    }
};
export default Spotify;

SearchBar.js:

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);
    }

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

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

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

export default SearchBar;

SearchResults:

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.searchResults} 
                    onAdd={this.props.onAdd}
                    isRemoval={false}/>
            </div>
        );
    }
}

export default SearchResults;

Again, thank you for your time!

Hello,

Check your JSX in SearchBar.js. You have a Search button, but you don’t have any event handlers associated with it, so it doesn’t run any code when you click.

Thanks for your response! That’s what I thought, however the project step guide says to add the handleTermChange to the input, and do nothing with the Search button. No following steps say to set up an event handler with that button, unless I missed it.

Edit: I updated the code to have onClick={this.props.onSearch} for the button jsx and now i get an jsonResponse undefined error, so it looks like that worked. Now i just need to figure out why jsonResponse is undefined -_-’

There’s a lot of steps on the project and it’s been awhile since I completed it, so I can’t speak specifically if it’s hidden in a hint, implied that it needs to be done, or maybe missed completely. Either way, the button needs to do something.

You have a search() method in your SearchBar component that calls this.props.onSearch() with the term from the component’s state already. That method is what needs to be hooked up to your button, not this.props.onSearch directly. Otherwise, the click event object gets sent as an argument rather than the component’s term from its state.

D’oh, you’re right, thank you for that. I updated it to be this.search. Thanks!