Hi all, I am working on the Jammming project in the Full Stack Engineer career path and have had an issue that others appear to have encountered, but sadly none of the related solutions have worked for them.
My error occurs when I try to save a playlist to Spotify with a name that is different from the default (the default name works). While most people have had issues in their Spotify components, I think mine actually has to do with my Playlist component but I cannot figure it out.
In terms of steps I have taken to troubleshoot so far I have:
- Reviewed the walkthrough video step by step
- Read through ~20-30 threads related to this issue and checked my code for the solutions used in those instances.
I would be enormously grateful if you could review my code and advise. It is as follows:
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.js';
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) {
if (this.state.playlistTracks.includes(track.id)) {
return;
} else {
const playlistUpdate = this.state.playlistTracks;
playlistUpdate.push(track);
this.setState({ playlistTracks: playlistUpdate });
}
}
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>
)
}
}
export default App;
Spotify.js:
const clientId = 'REDACTED';
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 is going to clear the parameters and allow 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(search) {
const accessToken = Spotify.getAccessToken();
return fetch(`https://api.spotify.com/v1/search?type=track&q=${search}`, {
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;
Playlist.js:
import React from 'react';
import './Playlist.css';
import TrackList from '../TrackList/TrackList';
class Playlist extends React.Component {
constructor(props) {
super(props);
this.handleNameChange = this.handleNameChange.bind(this);
}
handleNameChange(event) {
this.props.onNameChange(event.target.onChange)
}
render() {
return (
<div className="Playlist">
<input defaultValue={'New Playlist'}
onChange={this.handleNameChange} />
<TrackList tracks={this.props.playlistTracks}
onRemove={this.props.onRemove}
isRemoval={true}/>
<button className="Playlist-save"
onClick={this.props.onSave }>SAVE TO SPOTIFY</button>
</div>
)
}
}
export default Playlist