Jamming Search Problem

Guys I have problem, when I pressed search button it is not working. Anything i searched for didn’t show up.

App.js

import React from "react";

import "./App.css"; // Tell webpack that Button.js uses these styles

import { SearchResults } from "../SearchResults/SearchResults";

import { SearchBar } from "../SearchBar/SearchBar";

import { Playlist } from "../Playlist/Playlist";

import Spotify from '../../util/Spotify';

export 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(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>

  )

 }

}

SearchBar.js

import React from 'react';

import './SearchBar.css'; // Tell webpack that Button.js uses these styles

export class SearchBar extends React.Component {

    constructor(props) {

        super(props);

        this.state = {

        term: ''

        };

        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" onClick={this.search}>SEARCH</button>

            </div>

        );

    }

}

Spotify.js

const clientId = ""; // I have pasted here.

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(accessToken && 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,

            artists: 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;

Edit: Disregard this. I realize I had implemented bonus feature and therefore my code was a bit different from original project.

I think the problem is here in the SearchBar component:

    search() {

      this.props.onSearch(this.state.term);

    }

The onSearch function takes 2 arguments so you need to pass in playlistTracks also. Try:

    search() {

      this.props.onSearch(this.state.term,  this.props.playlistTracks);

    }

I have tried, still not working.

Double check your logic here. My bet is that if you add some logging to your Spotify.search() method, you’ll find that you’re never able to retrieve an accessToken, so it tries to send undefined as the access token. Note, you’d probably need to turn on Preserve Log in DevTools to see the logs because you’re accidentally creating a redirect loop.

      if(accessToken && expiresInMatch) {
//           ^ this will always be undefined

Since that block of code to extract the access token from the URL is never reached, accessToken is never set to anything other than undefined

The variable you mean to be checking there is accessTokenMatch

2 Likes

I realize now that I implemented some of the bonus features in the end and that’s probably where I added the second parameter to search-functions (to avoid getting tracks already in the list). So please disregard my suggestion and stick with the one parameter. Sorry for the confusion.

1 Like

Thank you, it’s definitely the logic problem.