I’m working on the Jammming project and I have an issue where upon searching the first time, the app returns results correctly. However, on another press of the search button, the entire app seems to reload and I lose any state that was already set. The playlist and search results both reset to the mock data I used to fill the original page.
App.js
import React, { useCallback, useState } from "react";
import { v4 as uuidv4 } from "uuid";
import "./App.css";
import Playlist from "./components/Playlist";
import SearchBar from "./components/SearchBar";
import SearchResults from "./components/SearchResults";
import Spotify from "./helpers/Spotify";
function App() {
const mockResults = [
{
id: 1,
playlistId: uuidv4(),
title: "Bat Country",
artist: "Avenged Sevenfold",
album: "Bat Country",
},
{
id: 2,
playlistId: uuidv4(),
title: "Enter Sandman",
artist: "Metallica",
album: "Metallica (Remastered)",
},
];
const mockPlaylist = [
{
id: 1,
playlistId: uuidv4(),
title: "Bat Country",
artist: "Avenged Sevenfold",
album: "Bat Country",
},
];
const [searchResults, setSearchResults] = useState(mockResults);
const [playlist, setPlaylist] = useState(mockPlaylist);
const [playlistName, setPlaylistName] = useState("New Playlist");
const handleAddSong = useCallback((newSong) => {
const trackWithId = { ...newSong, playlistId: uuidv4() };
setPlaylist((prevList) => [...prevList, trackWithId]);
}, []);
const handleRemoveSong = useCallback((songToRemove) => {
setPlaylist((prevList) => {
return prevList.filter(
(song) => song.playlistId !== songToRemove.playlistId
);
});
}, []);
const handleSearch = useCallback(async (term) => {
const resultTracks = await Spotify.search(term);
const resultTracksWithId = resultTracks.map((track) => {return {...track, playlistId: uuidv4()}});
setSearchResults( resultTracksWithId);
}, []);
const handlePlaylistNameChange = useCallback(
(newName) => setPlaylistName(newName),
[]
);
return (
<div className="App">
<header className="App-header">
<h1>Jammming</h1>
<p>Build your perfect Spotify playlists</p>
</header>
<div className="searchBar">
<SearchBar onSearch={handleSearch} />
</div>
<div className="App-body">
<SearchResults
tracks={searchResults}
action="+"
actionMethod={handleAddSong}
/>
<Playlist
tracks={playlist}
playlistName={playlistName}
changeName={handlePlaylistNameChange}
action="-"
actionMethod={handleRemoveSong}
/>
</div>
<footer>
{`Made with <3 by Kyle Buck | `}
<a href="https://github.com/BuckBuckGoose/jammming">Github</a>
</footer>
</div>
);
}
export default App;
Spotify.js
const clientId = "[removed from post]";
const redirectUri = "http://localhost:3000";
const baseUrl = "https://api.spotify.com/v1";
let accessToken;
const Spotify = {
getAccessToken() {
console.log(`Get: ${accessToken}`);
if (accessToken) {
console.log(`Storage: ${accessToken}`);
return accessToken;
}
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]);
window.setTimeout(() => (accessToken = ""), expiresIn * 1000);
window.history.pushState("Access Token", null, "/");
console.log(`Retrieve: ${accessToken}`);
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;
}
},
async search(term) {
const accessToken = Spotify.getAccessToken();
return await fetch(`${baseUrl}/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,
}));
});
},
async savePlaylist(name, trackUris) {
if (!name || !trackUris.length) {
return;
}
const accessToken = Spotify.getAccessToken();
const headers = { Authorization: `Bearer ${accessToken}` };
let userId;
return await fetch(`${baseUrl}/me`, { headers: headers })
.then((response) => response.json())
.then((jsonResponse) => {
userId = jsonResponse.id;
return fetch(`${baseUrl}/users/${userId}/playlists`, {
headers: headers,
method: "POST",
body: JSON.stringify({ name: name }),
})
.then((response) => response.json())
.then((jsonResponse) => {
const playlistId = jsonResponse.id;
return fetch(
`${baseUrl}/users/${userId}/playlists/${playlistId}/tracks`,
{
headers: headers,
method: "POST",
body: JSON.stringify({ uris: trackUris }),
}
);
});
});
},
};
export default Spotify;
SearchBar.js
import React, {useState} from 'react';
function SearchBar(props) {
const [searchText, setSearchText] = useState('');
const handleTextUpdate = (e) => setSearchText(e.target.value);
const handleButtonPress = (e) => {
e.preventDefault();
props.onSearch(searchText)};
return (
<form>
<input type='text' name='searchBar' id='searchBar' value={searchText} onChange={handleTextUpdate} />
<button type='button' name='searchButton' id='searchButton' onClick={handleButtonPress}>Search</button>
</form>
);
}
export default SearchBar;