Jammming Passing Data

Hello fellow learners!
I’ve been trying to complete the Jammming project but am having trouble passing the data stored in my searchresults array to the playlist array. I created an event handler to pass the searchresults array info to the playlist array when a track is clicked on in order to propogate that playlist. The way I organized my file structure the searchresults component and playlist component are siblings so I attempted to pass the data from the parent down to the children but upon doing so I get a syntheticbaseevent instead of a track with the correct info .

This is what the result looks like…


Does anyone know what the solution is to this?

Thanks for taking the time to review my code!

This is my Top level component “App”

import { useState, useCallback } from "react";
import SearchBar from "./SearchBar";
import SearchResults from "./SearchResults";
import Playlist from "./Playlist";
import styles from "../Styles/App.module.css";

function App() {
  const [playlistTracks, setPlaylistTracks] = useState([
    {
      song: "Go Baby",
      artist: "Lupe",
      album: "Food and Liqour",
      id: 1,
    },
  ]);
  const [searchResults, setSearchResults] = useState([
    {
      song: "Go Baby",
      artist: "Lupe",
      album: "Food and Liqour",
      id: 1,
    },
    {
      song: "Demon Days",
      artist: "Gorillaz",
      album: "Self titled",
      id: 2,
    },
  ]);

  const onAdd = (track) => {
    setPlaylistTracks((prevTracks) => [...prevTracks, track]);
    track.cancelable = false;
    console.log(`track is ${track}`);
  };

  return (
    <div id={styles.app}>
      <header>
        <h1>Spotify Playlist App</h1>
      </header>
      <SearchBar />
      <main>
        <SearchResults searchResults={searchResults} onAdd={onAdd} />
        <Playlist playlistTracks={playlistTracks} />
      </main>
    </div>
  );
}

export default App;

Search Results

import React from "react";
import styles from "../Styles/SearchResults.module.css";
import Tracklist from "./Tracklist";

function SearchResults(props) {
  return (
    <div id={styles.searchResults}>
      <h3>Results</h3>
      <Tracklist tracks={props.searchResults} onAdd={props.onAdd} />
    </div>
  );
}

export default SearchResults;

Playlist

import React from "react";
import styles from "../Styles/Playlist.module.css";
import Tracklist from "./Tracklist";

function Playlist(props) {
  const { playlistTracks, onRemove } = props;
  console.log(playlistTracks);
  return (
    <div id={styles.playlist}>
      <h3>Playlist</h3>
      <input type="text" />
      <Tracklist tracks={playlistTracks} />
      <button>Save to Spotify</button>
    </div>
  );
}

export default Playlist;

Tracklist

import React from "react";
import Track from "./Track";

function Tracklist(props) {
  return (
    <div>
      {props.tracks.map((track) => {
        return <Track track={track} onAdd={props.onAdd} />;
      })}
    </div>
  );
}

export default Tracklist;

Track

import React, { useCallback } from "react";
import styles from "../Styles/Track.module.css";

function Track(props) {
  const choice = () => {
    return <button onClick={props.onAdd}>+</button>;
  };

  return (
    <div id={styles.track}>
      <div id={styles.track_info}>
        <h4>{props.track.song}</h4>
        <p>
          {props.track.artist} | {props.track.album}
        </p>
      </div>
      {choice()}
    </div>
  );
}

export default Track;```

Reading through your code, I think I’ve found the source of the error.
Your use of the onAdd() function is as an event handler, which accept one parameter: the event object
I would rename onAdd() to addPlaylistTrack(), and in track add a new function to Track that calls addPlaylistTrack() with props.track passed as the argument. Like this:

import React from "react";
import styles from "../Styles/Track.module.css";

function Track({
  addPlaylistTrack,
  track
}) {
  const clickHandler = e => {
    e.preventDefault();
    e.stopPropagation();
    addPlaylistTrack(track);
  }

  return (
    <div id={styles.track}>
      <div id={styles.track_info}>
        <h4>{track.song}</h4>
        <p>
          {track.artist} | {track.album}
        </p>
      </div>
      <button onClick={clickHandler}>+</button>
    </div>
  );
}

export default Track;

And of course you would need to rename onAdd() in the other files, too.

If you have any more questions feel free to ask!

2 Likes

Yes, I think dyenamite is correct in that the issue lies with what argument is being provided to the onAdd function.

The onAdd function in App.js has one parameter. The reference to this onAdd function is passed as a prop in many components, but the function actually gets called in Track.js in the following statement,

return <button onClick={props.onAdd}>+</button>;

However, in the absence of an explicit argument to the onAdd function, the SyntheticEvent object is passed as the argument (For more on SyntheticEvent, see: Older Documentation and New Documentation),

Perhaps, one way to pass the argument explicitly would be:

return <button onClick={() => props.onAdd(props.track)}>+</button>;

This way when the button is clicked, the arrow function will execute and in turn will call onAdd with the correct argument. However, this would pass props.track as the argument and I wonder if the statement

track.cancelable = false;

in App.js would serve any useful purpose. Since there is no live version to interact and tinker with, so I can’t really experiment to see what happens. Just sharing some thoughts.

2 Likes

Thank you a million! This was an easy solution but I hadn’t been able to figure it out. :pray:t5:

Under this new configuration, any idea how i could change that plus sign to. a minus and add a different event handler? i was thinking use the ternary operator but i havent been able to get it either.

As of now all changed is the return button from

return <button onClick={props.onAdd}>+</button>;

to this

return <button onClick={() => props.onAdd(props.track)}>+</button>;

thanks again! I might been in over my head :slight_smile:

You could try passing a string into Tracklist as a prop, and passing it all the way to the Track component.
Then you can use a ternary operator as you said. Something like this:

switch (TracklistType) {
  case "Search": {
    return <button onClick={() => props.onAdd(props.track)}>+</button>;
  }
  case "Playlist": {
    return <button onClick={() => props.onRemove(props.track)}>-</button>;
  }
  default: {
    return undefined;
  }
}
1 Like

Thanks again! That solve it :pray:t5:
If you don’t mind Ill ask another question.

So I passed my onRemove prop and got it to the track successfully but when I click the remove button it only removes the name, artist and album and not the actual track card as seen below…

but if i add multiple tracks and then try to remove one, all but one track “card” disappear… like so

I have a console log in my onRemove function and it is logging the playlistTracks array as you can see above

this is what the onRemove function looks like

const onRemove = (e) => {
    console.log(playlistTracks);
    const target = e.target.value;
    setPlaylistTracks([playlistTracks.filter((song) => song.id == e.id)]);
  };

whats going on here?