Jammming Project: can't update state

link : https://www.codecademy.com/paths/web-development/tracks/front-end-applications-with-react/modules/jammming/projects/jammming-prj

After thinking I had finished the project I have realised that after clicking the “Save to Spotify” button the playlist name doesn’t change. The .savePlaylist() method in App.js should change it (this.state.playlistName) back to its default value of ‘Playlist Name’. I have tried using the .setState() method and the .updatePlaylistName() method that was created for this project but neither will work. Everything else on the site and in the .setState() method work correctly, I just can’t seem to reset the playlist name. Please help me!

Here is the App.js code:

import React from 'react';
import './App.css';
import SearchBar from '../SearchBar/SearchBar';
import SearchResults from '../SearchResults/SearchResults';
import Playlist from '../Playlist/Playlist';
import Spotify from '../../util/Spotify';

Spotify.getAccessToken();

class App extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      searchResults: [],
      playlistName: 'Playlist Name',
      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: 'Playlist Name',
        playlistTracks: [] 
      })
    })
  }

  search(searchTerm) {
    Spotify.search(searchTerm).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;

And here is the Playlist.js code for the component:

import React from 'react';
import TrackList from '../TrackList/TrackList';
import './Playlist.css';

class Playlist extends React.Component {
    constructor(props) {
        super(props);
        this.handleNameChange = this.handleNameChange.bind(this);
    }

    handleNameChange(e) {
        this.props.onNameChange(e.target.value);
    }

    render() {
        return (
            <div className="Playlist">
                <input defaultValue={this.props.playlistName} onChange={this.handleNameChange} />
                <TrackList tracks={this.props.playlistTracks} onRemove={this.props.onRemove}/>
                <button className="Playlist-save" onClick={this.props.onSave}>SAVE TO SPOTIFY</button>
            </div>
        )
    }
};

export default Playlist;
1 Like

I just used value instead of defaultValue.

Hi there,
I know this is a few months on from you posting your issue with the Jamming project, but I wondered if you ever managed to resolve the problem? I have got the same issue regarding the playlist name not changing.
I tried the suggestion of using value instead of defaultValue but that didn’t work for me.
I would love to hear if you found a solution as it was such a long project and so frustrating to not be able to complete it 100%!

The value on the playlist name input element needs to be a variable that leads back to the this.state.playlistName. make sure you set a “name” attribute on the input element that is also “playlistName”. Also, you need to have an onChange function. Then, in the file that holds your state (prop app.js), you can just make a function that

const updateStateInputField = e => {
this.setState({
[e.target.name]: e.target.value
})
}

this way you can use the same function for both input elements dynamically.

Hello,
Thank you for your reply!
However havingf studied it closely I’m still not sure what I am missing -
I have got an onChange function and a function in app.js which updates this. I feel it must be something to do with the savePlaylist function as this is where it should be reset back to ‘New Playlist’ when the track list is set back to an empty array?
However I may be missing something from your explanation.
Here is my app.js and my playlist.js - if you have the time to look it over and see if I haven’t grasped what you have suggested then I would be very grateful for some more advice!
I have also noticed something strange, which may or may not be related to the other problem in my code is whenever the search button is first pressed it clears the search term but then accepts it the second time?! hmmm…
Here is my code
App.js

import React from 'react';
import './App.css';

import Playlist from '../Playlist/Playlist';
import SearchBar from '../SearchBar/SearchBar';
import SearchResults from '../SearchResults/SearchResults';
import Spotify from '../../util/Spotify';

class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      searchResults: [],
      playlistName: 'New Playlist',
      playlistTracks: []
    };

    this.search = this.search.bind(this);
    this.addTrack = this.addTrack.bind(this);
    this.removeTrack = this.removeTrack.bind(this);
    this.updatePlaylistName = this.updatePlaylistName.bind(this);
    this.savePlaylist = this.savePlaylist.bind(this);
  }

  search(term) {
    Spotify.search(term).then(searchResults => {
      this.setState({searchResults: searchResults});
    });
  }

  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: []
      });
    });
  }

  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}
                      onNameChange={this.updatePlaylistName}
                      onRemove={this.removeTrack}
                      onSave={this.savePlaylist} />
          </div>
        </div>
      </div>
    );
  }
}

export default App;

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){
    const name = event.target.value;
    this.props.onNameChange(name)
  }

  render(){
    return(
      <div className="Playlist">
  <input onChange={this.handleNameChange}
          defaultValue={"New Playlist"}
          />
  <TrackList tracks={this.props.playlistTracks}
             onAdd={this.props.onAdd}
             onRemove={this.props.onRemove}
             isRemoval={true}/>
  <button className="Playlist-save"
          onClick = {this.props.onSave}>SAVE TO SPOTIFY</button>
</div>
    )
  }
}

export default Playlist;

A couple things -
First, I would refactor your playlist into a stateless functional component since it is a presentational component. To do that, in Playlist.js, just get rid of constructor method altogether, get rid of handleNameChange function altogether, and get rid of the render method BUT NOT THE RETURN STATEMENT INSIDE! then change the class Playlist component to an arrow function called Playlist that takes one parameter, props. Get rid of all references to this in the return statement.
Second, in app.js, change the name of updatePlaylist name function to updateInputText that takes an event. Inside that function add the code i showed you above,
const updateInputText(e) {
this.setState({
[e.target.name]: e.target.value
})
}
bind it in the constructor, and then pass it as a prop to both the SearchBar component and the Playlistcomponent.
Last, in Playlist.js, in the input element, change defaultValue to value and set it to {props.playlistName}, put that “new playlist” text in a placeholder attribute, and set a name attribute to “playlistName”.

I had this problem, but I got it working correctly! Here’s my App.js and Playlist.js so it can be compared to the original post…

App.js: Changes made in savePlaylist

import React from 'react';
//import logo from './logo.svg';
import './App.css';
//import { render } from 'react-dom';
import SearchBar from '../SearchBar/SearchBar.js';
import SearchResults from '../SearchResults/SearchResults.js';
import Playlist from '../Playlist/Playlist.js';
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) {
    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);
    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;

Playlist.js: Changes defaultValue to value on the input element

import React from 'react';
import TrackList from '../TrackList/TrackList.js';
import './Playlist.css';

class Playlist extends React.Component {
    constructor(props) {
        super(props);

        this.handleNameChange = this.handleNameChange.bind(this);
    }

    handleNameChange(event) {
        this.props.onNameChange(event.target.value);
    }

    render() {
        return (
            <div className="Playlist">
                <input value={this.props.playlistName} 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;