React Jammming Search issue

Hello,

I have finally reached the end of this project - and I had to follow the video nearly every step of the way! Too advanced for me. But I did finish it and I have an odd issue that I cannot understand.

When I click the Search button, whether I have entered a search term or not, it produces a random results list of as many songs or albums which contain the word ’ object ’ which is odd. Usually on loading that, it will refresh and wipe the slate clean, and if I do it again they’ll stick around. The app was working enough up until the last few steps so the issue must be in the Spotify.js component, code and screenshot below.

Spotify.js

const clientId = 'removedForThisPost';
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]);
            //clears the params, allowing us to grab a new access token once 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;

Hopefully one of you lovely people can help!

thanks

Hi Lee,

What you could do is adding a console to the search function, right before the line where you map the tracks in order to see what you get from Spotify:

console.log(jsonResponse.tracks.items);
return jsonResponse.tracks.items.map(track => ({ ...

If the console shows the expected results, the problem is probably within the component that calls the search function, and that is probably the App component.

hi, thanks for responding,

I did what you said, and it is console logging the list of tracks/albums that are in the screenshot above - every time it loads, its the same list… I’m at a complete loss.

Could you post your code from App.js here, too?

1 Like
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) {
        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;
        tracks = tracks.filter(currentTrack => currentTrack.id !== track.id);
        this.setState({playlistTracks: tracks})
    }

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

    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;

These are both fine.
Let’s see what you get as a term from SearchBar: If you add a console to the search function in App.js:

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

What do you get?

wahey! :grin:

so console logging the term gave a strange response - but was still connected to the button correctly. Looking at the button, I had set the onClick equal to {this.props.Onsearch} instead of {this.search}, which was messing it up. It now does work, although there is a still a minor issue of, once I click Search for the first time after loading, it refreshes the page, and clears the search box.

Thank you for your help so far - any idea why it might refresh the page?

That’s because you need to assign the access token to the variable ‘accessToken’ on first search.
When you start a search the function getAccessToken is called first:

If that is not yet assigned to the global variable, it enters the else block of the function:

1 Like

yeah, so I have a global accessToken variable set empty on load, and once search is clicked only then does it try find the accessToken for the user - if there isn’t one yet - it refreshes the page, now with the accessToken from the user account, set into the URL. This would work better as an app if maybe there was a ‘Connect’ button which would run the getAccessToken method first, then once the user is connected to their Spotify account, and the token is set in the variable, then we are ready to search without any more interruptions.

what do you think :slight_smile:

The variable will be cleared on every reload of the page whether you set it with a button first or by an initial search, so I guess it doesn’t make much of a difference…

1 Like

I know, but this way feels more user friendly, rather than it removing the search term on the first search.

Thank you for all your help :smiley:

1 Like