The beauty and simplicity of the Functional Component

[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