Search code examples
javascriptreactjssession-storage

Issue with Saving and Reloading a Value Locally


So in one of my projects, I am attempting to add the functionality of saving track elements from the Spotify API to the session, then pulling them again. I inserted a console log to find out that even when the element from the session storage is undefined, it still enters the if block in the checkPlaylistName() method. The console log in the render statement is run twice, the first time, it passes an empty array, which is what I want when no elements are passed, the second time, it passes undefined for some reason. That's what causes an error in a different component, saying it is passed undefined. If you need the whole repo, you can find it here. Otherwise, this is the code causing the issues:

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.js';


class App extends React.Component {
	constructor(props) {
		super(props);
		this.state = {
			searchResults: [],
			playlistName: 'New Playlist',
			playlistTracks: [],
			term: ''
		}
		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) {
		const addingTrack = (track) => this.setState({playlistTracks: [...this.state.playlistTracks, track]});
		addingTrack(track);
		this.removeTrack(track, false);
		sessionStorage.setItem("playlistTracks", this.state.playlistTracks);
	}

	removeTrack(track, removePlaylist) {
		if(removePlaylist) {
			const ids = this.collectIds(true);
			let trackIndex = -1;
			for(let i = 0; i < ids.length; i++) {
				if (ids[i] === track.id) {
					trackIndex = i;
				}
			}
			if (trackIndex !== -1) {
				const newPlaylist = this.state.playlistTracks;
				newPlaylist.splice(trackIndex, 1);
				this.setState({playlistTracks: newPlaylist});
				this.search(this.state.term);
			}
		} else {
			const ids = this.collectIds(false);
			let trackIndex = -1;
			for(let i = 0; i < ids.length; i++) {
				if (ids[i] === track.id) {
					trackIndex = i;
				}
			}
			if (trackIndex !== -1) {
				const newResults = this.state.searchResults;
				newResults.splice(trackIndex, 1);
				this.setState({searchResults: newResults});
			}
		}
		sessionStorage.setItem("playlistTracks", this.state.playlistTracks);
	}

	collectIds(removePlaylist) {
		let ids = [];
		if(removePlaylist) {
			this.state.playlistTracks.map(track => ids.push(track.id));
		} else {
			this.state.searchResults.map(track => ids.push(track.id));
		}
		return ids;
	}

	updatePlaylistName(name) {
		this.setState({playlistName: name});
		sessionStorage.setItem("playlistName", name);
	}

	savePlaylist() {
		let trackURIs = [];
		for(let i = 0; i < this.state.playlistTracks.length; i++) {
			trackURIs.push(this.state.playlistTracks[i].uri);
		}
		Spotify.savePlaylist(this.state.playlistName, trackURIs);
		this.setState({playlistName: 'New Playlist', playlistTracks: []});
		sessionStorage.removeItem("playlistTracks");
		sessionStorage.removeItem("playlistName");
	}

	async search(term) {
		const results = await Spotify.search(term);
		this.setState({searchResults: results});
		const resultIds = this.collectIds(false);
		const playlistIds = this.collectIds(true);
		let indexes = [];
		for(let i = 0; i < resultIds.length; i++) {
			for(let j = 0; j < playlistIds.length; j++) {
				if (resultIds[i] === playlistIds[j]) {
					indexes.push(i);
				}
			}
		}
		if(indexes.length > 0) {
			for (let k = 0; k < indexes.length; k++) {
				results.splice(indexes[k], 1);
			}
		}
		this.setState({searchResults: results});
		this.setState({term: term});
	}

	checkTracks() {
		if (sessionStorage.getItem("playlistTracks") !== undefined) {
			const tracks = sessionStorage.getItem("playlistTracks");
			this.setState({playlistTracks: tracks});
		}
		return this.state.playlistTracks;
	}

	checkPlaylistName() {
		const savedName = sessionStorage.getItem("playlistName");
		if (savedName !== null || savedName !== undefined) {
			this.setState({playlistName: savedName});
			console.log("hi");
		}
		return this.state.playlistName;
	}

	render() {
		return (
			<div id="root">
  				<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} onRemove={this.removeTrack} />
      					{console.log(this.checkTracks())}
      					<Playlist 
      						playlistName={this.checkPlaylistName()}
      						playlistTracks={this.checkTracks()}
      						onRemove={this.removeTrack}
      						onNameChange={this.updatePlaylistName}
      						onSave={this.savePlaylist}
      					/>
    				</div>
  				</div>
			</div>
		);
	}
}

export default App;


Solution

  • setState function is asynchronous (https://medium.com/@baphemot/understanding-reactjs-setstate-a4640451865b), therefore you are returning old state in checkTracks method.

    Solution: Don't setState in your checkTracks method, make it:

    if (sessionStorage.getItem("playlistTracks") !== undefined) {
        return sessionStorage.getItem("playlistTracks");
    }
    return [];
    

    And use this method in constructor to define state.

    Also in your way you are setting state (via checkTracks method) in render function which causes an infinite loop.