[I have a little more work to do to save and remove tracks from Playlist, but you will get the idea.]
I wanted to change it up a little bit. I was curious about the functional component approach. So I tried it on this project. Below is the code. I included some comments showing what the class approach would have looked like at key points. The key things are no class, constructor, ‘this’, or binding methods to ‘this.’ props are still used, liberally used, but hidden behind destructing. It is just JavaScript.
//app.js
import { useState, useEffect } from 'react';
import './App.css';
import SearchBar from '../SearchBar/SearchBar';
import SearchResults from '../SearchResults/SearchResults';
import Playlist from '../Playlist/Playlist.js';
import Spotify from '../../util/Spotify.js';
const App = () => {
// class state:
// this.state = {
// searchResults: [],
// playlistName: 'New Playlist',
// playlistTracks: [],
// };
//functional state:
const [playlistName, setPlaylistName] = useState('');
const [TrackList, setTrackList] = useState('');
const [searchResults, setsearchResults] = useState([]);
const [playlistTracks, setPlaylistTracks] = useState([]);
const addTrack = (track) => {
if (playlistTracks.includes(track.id)) {
return;
} else {
const playlistUpdate = playlistTracks
playlistUpdate.push(track);
//this.setState({ playlistTracks: playlistUpdate });
setPlaylistTracks(playlistUpdate)
}
}
const removeTrack = (track) => {
let tracks = playlistTracks;
tracks = tracks.filter((trackIndex) => trackIndex.id !== track.id);
setPlaylistTracks(tracks);
};
const updatePlaylistName = (name) => {
setPlaylistName(name)
};
const savePlaylist = () => {
const trackURIs = playlistTracks.map((track) => track.uri);
// return trackURIs;
Spotify.savePlaylist(playlistName, trackURIs).then(() => {
//this.setState({ playlistName: 'New Playlist', playlistTracks: [] });
setPlaylistName('New Playlist')
setPlaylistTracks([])
});
};
const search = (searchTerm) => {
Spotify.search(searchTerm).then((searchResults) => {
//this.setState({ searchResults: searchResults });
setsearchResults(searchResults)
});
}
return (
<div>
<h1>
Ja<span className="highlight">mmm</span>ing
</h1>
<div className="App">
<SearchBar onSearch={search} />
<div className="App-playlist">
<SearchResults
searchResults={searchResults}
onAdd={addTrack} />
<Playlist
playlistName={playlistName}
playlistTracks={playlistTracks}
onRemove={removeTrack}
onAdd={addTrack}
onNameChange={updatePlaylistName}
onSave={savePlaylist} />
</div>
</div>
</div>
);
};
export default App;
//SearchBar.js
import { useState, useEffect } from 'react';
import './SearchBar.css';
const SearchBar = ({onSearch}) => {
//state
const [searchTerm, setSearchTerm] = useState('');
const search = () => {
//this.props.onSearch(this.state.term);
onSearch(searchTerm); //put term in useState
};
const handleTermChange = (e) => {
//this.setState({ term: e.target.value });
setSearchTerm(e.target.value)
}
return (
<div className="SearchBar">
<input placeholder="Enter A Song, Album, or Artist" onChange={handleTermChange} />
<button className="SearchButton" onClick={search}>
SEARCH
</button>
</div>
);
};
export default SearchBar
//searchResults.js
import TrackList from '../TrackList/TrackList';
import './SearchResults.css';
const SearchResults = ({searchResults, addTrack}) => {
return (
<div className="SearchResults">
<h2>Results</h2>
<TrackList
tracks={searchResults}
onAdd={addTrack}
isRemoval={false} />
</div>
);
};
export default SearchResults;
//playlist.js
import './Playlist.css'
import TrackList from '../TrackList/TrackList'
//From app.js: props = playlistName, playlistTrack, onRemove, onNameSave, onSave
const Playlist = ({playlistName, playlistTracks, onNameChange, onAdd, onRemove, onSave}) => {
const handleNameChange = (event) => {
onNameChange(event.target.value);
}
return (
<div className="Playlist">
<input
id="Playlist-name"
defaultValue={playlistName}
onChange={handleNameChange} />
<TrackList
tracks={playlistTracks}
onAdd ={onAdd}
onRemove={onRemove}
isRemoval={true} />
<button
className="Playlist-save"
onClick={onSave}>
SAVE TO SPOTIFY
</button>
</div>
);
}
export default Playlist
//TrackList.js
import './TrackList.css';
import Track from '../Track/Track'
// from Playlist: tracks={props.playlistTracks} onAdd={addTrack}, onRemove={props.onRemove} isRemoval={true}
const TrackList = ( {tracks, onAdd, onRemove, isRemoval}) => {
let listOfTracks = tracks.map(track => {
return <Track
track={track}
key={track.id}
onAdd={onAdd}
onRemove={onRemove}
isRemoval={isRemoval}
/>
})
return <div className="TrackList">
{listOfTracks}
</div>;
};
export default TrackList
//Track.js
import './Track.css';
const Track = ({isRemoval, onAdd, onRemove, track}) => {
const renderAction = () => {
if (isRemoval) {
return (
<button className="Track-action" onClick={removeTrack}>
-
</button>
);
} else {
return (
<button className="Track-action" onClick={addTrack}>
+
</button>
);
}
}
const addTrack = (event) => {
onAdd(track);
}
const removeTrack = (event) => {
onRemove(track);
}
//props = track.name, track.artist, track.album
return (
<div className="Track">
<div className="Track-information">
<h3>{track.name}</h3>
<p>
{track.artist} | {track.album}
</p>
</div>
{renderAction()}
</div>
);
};
export default Track