Jamming not pulling from Spotify API, no results show up when I type into the search bar

I have nearly finished the Jammming project but my website will not show any results when I type into the search bar. I feel like it might be an error with calling the APIs from Spotify or with my methods. I am very new to Javascript so I’m unsure. Below is my code from the main JS files. I have not included my clientID and similar codes for obvious reasons.

App.js

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

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) {
    let tracks = this.state.playlistTracks;
    if (tracks.find(savedTrack => savedTrack.id === track.id)) {
      return;
    }

    tracks.push(track);
    this.setState({ playlistTracks: tracks });
  }

  removeTrack(track) {
    let tracks = this.state.playlistTracks;
    if (tracks.find(savedTrack => savedTrack.id === track.id)) {
      return;
    }

    tracks.pop(track);
    this.setState({ playlistTracks: tracks });
  }

  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}
            />
        </div>
      </div>
    </div>
    )
  }
}

export default App;

Spotify.js

const clientId = '';
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;
        }

        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;

SearchBar.js

import React from 'react';
import Track from '../Track/Track.js';
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">SEARCH</button>
            </div >
        )
    }
}

export default SearchBar;

Were there any errors in the browser’s console? There might not be if there aren’t any issues beyond the one I see in App.js, but I didn’t review the other files because I think you would have mentioned an error.

Double check your use of SearchResults vs searchResults when dealing with App’s state

Click here for more details and debugging strategies if you'd like

Since you specifically mentioned your search results, it’s a good idea to focus on the where the data is stored and then go from there to where it gets updated and to where it gets passed to other components. Adding logging to verify expected results is a good idea many times too.

App.js

  • App’s state object is initialized in the constructor with a SearchResults property set to an empty array
  • this.state.SearchResults is passed to another component, so the name matches up so far
  • search() sets the state of the property searchResults

The search results are being stored in a different property of state because of the letter casing: searchResults vs SearchResults
Example:

const myObject = {
  test1: 'hi',
  Test1: 'hi2'
};

console.log(myObject.test1); // hi
console.log(myObject.Test1); // hi2

This demonstrates what is happening. Saving the results to a different property means your other component never receives the new information, assuming the rest of your code is working as intended.

2 Likes

I understand your answer and fixed it. Nothing happens when I click search on the site itself. Could it be an issue with the button?

That’s a good one to fix next for sure. Right now there aren’t any events hooked up to the button, so it does nothing. That’s one of those times when logging can help give a clue, like putting a console.log() in your button handler and when you see it doesn’t print to console, you know it never gets called.

I went ahead and added onClick={this.search} to the and the button now works. No results show up though. Upon clicking it the first time, Spotify created a page asking permission for the API. After that, no results show up.

So no errors in the console? No other clues so far? If there aren’t any obvious clues in the browser’s console, you need to make your own by adding some logging to figure out if what you think should be called is being called, if the values you think you should be receiving are being received, etc.

There are no errors in the console. Where would I add the console.log() statement? In the handleTermChange function?

That’s one place, sure. You could have them at the start of a lot of your methods to see what is being run too. For example, the search() to make sure it’s being called. You could have it in your App’s search() method to see what values it received when it was called too. The idea is to get a good idea of the flow of your program. There’s also a handy extension called React Developer Tools that let’s you see the props that are being passed to the different components. That can help with debugging too.

I can tell you that I just copied the three files you posted into my project, corrected App’s searchResults vs SearchResults state issue, added the onClick event for the search button, and I’m getting results back. I didn’t test saving a playlist, but your search method in Spotify is working.

That’s odd that your fixes worked in your file but not mine. Here is what I wrote based on your help.

App.js

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

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) {
    let tracks = this.state.playlistTracks;
    if (tracks.find(savedTrack => savedTrack.id === track.id)) {
      return;
    }

    tracks.push(track);
    this.setState({ playlistTracks: tracks });
  }

  removeTrack(track) {
    let tracks = this.state.playlistTracks;
    if (tracks.find(savedTrack => savedTrack.id === track.id)) {
      return;
    }

    tracks.pop(track);
    this.setState({ playlistTracks: tracks });
  }

  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}
            />
        </div>
      </div>
    </div>
    )
  }
}

export default App;

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

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

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

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

export default SearchBar;

Those 2 files are working for me. Just copy/pasted your version into my project, along with your original Spotify.js you posted (and my clientId)

Remember that after you authorize, you’re taken back to the page and the search results will be empty. You have to perform the search again to get results after getting the token. (Just to be sure we’re on the same page) If we are on the same page with the expected behavior, then you may need to look elsewhere for your issues. Follow the data