Jamming Project is not pulling any data from spotify's API. Search results don't bring in anything

I just finished the Jamming project. The components and lifecycle methods were good practice for me. The POST, GET, and other methods to draw information from the API were very unfamiliar to me. Although the site runs, it will not bring any results when I try to use the search bar. I can’t seem to find anything when I use Chrome Dev Tools either.

App.js

//import logo from '../../logo.svg';
import './App.css';
import { SearchBar } from '../SearchBar/SearchBar';
import { SearchResults } from '../SearchResults/SearchResults';
import { Playlist } from '../Playlist/Playlist';
import Spotify from '../../util/Spotify';
import React from 'react';

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

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

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

export default App;

Spotify.js

let accessToken;
const clientID = 'x';
const redirectURI = 'http://localhost:3000/';

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 = `Back in your conditional statement, redirect users to the following URL:
                              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 './SearchBar.css';

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

SearchResults.js

import React from 'react';
import './SearchResults.css';
import { TrackList } from '../TrackList/TrackList';

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

The instructions for Jammming don’t specifically tell you to do it, but you need to add a click event to the Search button to start the process.

There was a step in the instructions where they wanted you to print something to the console to make sure it worked, which would have required setting up the click event though. Once you add the click event, you can begin testing and debugging your application. Be sure to continue reviewing the browser’s console if there are any issues.

1 Like

That was too obvious. Look at the button line on the SearchBar component. I added `onClick={this.search} which still doesn’t work. I get an error on the browser that says ‘TypeError: Cannot read property ‘term’ of null’.

That would make sense if you immediately click Search without filling in the input box. What’s the value of this.state.term if the handleTermChange() method is never called below? Where is the default value normally set? Hint: you’re missing something in the constructor.

Click for another hint

this.state and this.state.term doesn’t exist until handleTermChange() is called because you didn’t set the initial value of the this.state object in the constructor. This means that clicking the Search button without filling in the input box that triggers handleTermChange() causes your search() method to attempt to read the term property of this.state, which doesn’t exist yet.

Click for another hint

You should add something like this to the constructor:

this.state = { term: '' };

Now this.state.term will have a value (even if it’s an empty string), even without the user typing anything in the input box.

This may not be the last of the issues, but it should get you to the next point to continue testing and debugging.

1 Like

Thank you for that. I still run into the issue of no results returning when I search an artist.

Keep in mind that after you click Search the first time, it will direct you to Spotify for authentication. After being authenticated, it will redirect back to your application and you’ll have to search again. It won’t remember what you searched for, but it should have the token it needs for a request to the Spotify API now. Also, whenever the token is expired (a certain time period or you refreshing the browser, etc.), it will need to redirect back to Spotify. Sometimes that redirect to Spotify and back to your application is very quick because Spotify won’t have to ask for authentication again, but it still means your application will need you to search again after getting the new token.

Continue the debugging and testing process. Don’t forget to look in the browser’s console after you click Search, get the token, then search for an artist or song again.

1 Like

I went ahead and found this in the console when I tried to look up an artist,
“Resource interpreted as Stylesheet but transferred with MIME type text/html:”.

Where in your code did this error occur? Look at the traceback in the console, to which component, which line. It will help you identify where the problem is.

I have been doing this but I’m nowhere near knowledgeable enough to understand what has gone wrong. The extent of my react knowledge is from this course’s unit.

Can you share the whole error message you’re getting? It should tell you the line in your code where this error occurred. Then paste the code it is referring to, and we might be able to work out what it is referring to.

Also, if you are using Google Chrome Developer Tools, install the React developer tool extension. It has a components tab (in the line where there is the “console” tab, select the >> button and from the dropdown menu open Components), that you navigate your component tree structure and see what props are being passed between them. This is another way to check what might be going wrong if the artist isn’t being set properly.

Another debugging tool is to use console.log() at different points in your code to see what value is being grabbed and passed between your props.

One other thing is to watch the video walk through and check your code against the instructors to see if there is a mistype somewhere.

If search isn’t working there are several possible fail points:

  1. Spotify component may have failed to successfully fetch the data.
  2. may not have saved the data into state correctly
  3. components may not have passed the data as props
  4. all of the above may be correct, however may not have rendered the data correctly to the screen (eg in the Track component).

Can you see the problem here in your Spotify component ? your comment is saved as part of your accessUrl.

Can you take a look? Pressing search and nothing happened, I added in the
SearchBar.js

<button className="SearchButton" onClick={this.props.search}>SEARCH</button>

Spotyfy.js:

const clientId = 'client id';
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 accsess 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(); //konwertujemy na jsona
        }).then(jsonResponse => {
            if (!jsonResponse.tracks) { //wykrzyknik mówi, że to jest fałsz - nie zawiera wyników
                return []; //zwracamy pustą array
            } 
            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) { //sprawdzamy czy jest puste - nie ma name i array trackUris jest pusta - length zwraca nam ilosć obiektów
            return;
        }

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

        return fetch('https://api.spotify.com/v1/me', { headers: headers }
        ).then(response => response.json() // zamieniamy na jsona
        ).then(jsonResponse => {
            userId = jsonResponse.id; // zapisujemy userId jako to co nam zwrócił json
            return fetch(`https://api.spotify.com/v1/users/${userId}/playlists`,
            {
                headers: headers,
                method: 'POST',
                body: JSON.stringify({ name: name}) // imię jakie użytkownik nadaje playliście
            }).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;

@gasiopr what errors are you seeing in your console? What troubleshooting techniques have you already tried, and what was the outcome?

As I wrote earlier, there are many different things that could cause the app to fail, so you need to troubleshoot your app to see where the fail point is.

In addition to the above points - you also need to check your Spotify App settings (in the Spotify developer dashboard) to make sure you’ve got the clientId and redirectUri set to the right values as per the Spotify App settings.

how can I check if it connects with spotify?
when I log to the spotify

there is a note:

No data available.

Check back when you’ve made some
requests using this app for data.

In console there is only one thing:

index.js:1 Warning: Invalid DOM property `class`. Did you mean `className`?
    at div
    at TrackList (http://localhost:3000/static/js/main.chunk.js:1586:1)
    at div
    at SearchResults (http://localhost:3000/static/js/main.chunk.js:1097:1)
    at div
    at div
    at div
    at App (http://localhost:3000/static/js/main.chunk.js:288:5)

This console warning indicates you need to change ‘class’ to ‘className’ in the return statement of your TrackList component’. While JSX in React looks a lot like HTML, there are a few differences - and one of those is that class is called className.

Look at the logic of your code, and work out where it makes a call to the Spotify API, where the data is stored, and put a console.log() message to see if the data has been collected properly. Do this at each relevant step of your code till you can isolate what is not working.

A good starting place might be in your getAccessToken() method of the Spotify component. In the line before return accessToken insert console.log(accessToken) then look at the console in your browser as you press the search button, and see if a message is logged. (Note, there are several places in your code where you can check the value of the accessToken… check in multiple places).

If no message is logged to console, it means something has gone wrong prior to that step, as the code has not run. So you need to look at the code prior to this step - using similar debugging techniques - to identify which parts of your code are working, and which aren’t.

If a message is logged to console: is the value of the accessToken what you expected it to be? If so, then this part of the code is working, and you move on to the next bit of the code, testing each step until you find the bit that is not working.

If the value stored as accessToken is not what you expected, then that can tell you the process to get the accessToken is the problem.

Note, I’m using the accessToken as one example of something you can test to see if your app is connecting to Spotify and saving the correct data … however this process is the same for whatever you want to test… userId, playlistId, term

To learn more about debugging javascript, check out codecademy’s 1 hr How to Debug JavaScript Errors course.

What I meant about checking the Spotify settings is that you might need to double check that you have used the same clientId and redirectUri in the settings of your app on Spotify (logged into Spotify, in the developer dashboard, the app you’ve registered there) to the code you’ve written here in the Spotify.js component.

At one point I think I had an issue because the redirectUri in one of these places had a trailing slash / and the other didn’t… that threw a problem for me.

Thank you, it’s all the same in the Spotify developer and in the file double and triple check :slight_smile:
I came to seomething new
error:
Uncaught TypeError: Cannot read property ‘then’ of undefined

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

And you know what I can’t console.log anything from spotify.js and cant figure it out why?

This is an error I got many times doing this project, as you fix the error in one place, and can then find there is then the same error in another place. Here’s the documentation on TypeError

Basically, your error means that something in the code before the .then is not resolving properly ( is currently returning as ‘undefined’, so .then doesn’t work). It could be:

  • Spotify component has not been exported & imported?
  • A problem with the Spotify.savePlayList method (error in what it is returning)?
  • A problem accessing this.state.playlistName - either state has not been initialised correctly, or state has not been updated properly from the input?
  • trackUris might have an error in how it’s been initialised (e.g. if there are no playlistTracks to map)…

So your app is failing before any of the Spotify methods are run?

Trace back each step of what is going on in your app: where is it working up to? If console.log isn’t working in Spotify’s methods, then console.log in relevant places in App.js to find out where & why it’s failing.

The first time the App calls on Spotify is in the search method. So check if the App is correctly passing the search term in the search method.