Reactjs, Passing data(id) from one component to another in order to download a song

Viewed 80

Hi I am new to Reactjs and I'm practicing on a project of a music website. But there is an issue which I'm stuck with. Well there is a song page where I should show some songCards.

import React from 'react'
import SongCard from '../components/SongCard';
import { songsList } from '../components/songsList';

//this is first object of the songsList file which i hard coded instead of using api:

export const songsList =[
{
    id : 1,
    url : './components/Player/Songs/Homayoun Shajarian - Souvashoun.mp3',
    image : require('../assets/Souvashoun.jfif').default,
    name : 'x',
    singer : 'y',
    style :'z',
    lyrics :'v',
    source : './Palyer/Songs/Homayoun Shajarian - Souvashoun.mp3'


}];

export default function Songs() {
    return(
        <div>
        <SongCard className='col-md-6' id={songsList[0].id} image={songsList[0].image} name={songsList[0].name} singer={songsList[0].singer}/><br/>
        <SongCard className='col-md-6' id={songsList[1].id} image={songsList[1].image} name={songsList[1].name} singer={songsList[1].singer}/><br/>
        <SongCard className='col-md-6' id={songsList[2].id} image={songsList[2].image} name={songsList[2].name} singer={songsList[2].singer}/><br/>
        </div>
        );
}

and in the SongCard which I'll attach the code bellow, there is a button intended to route us to a downloadMusic page in order to download the specific song that it's button was clicked:

import React from 'react'
import {Card, Button} from 'react-bootstrap';
import styled from 'styled-components';
import {BrowserRouter as Router} from 'react-router-dom';

function SongCard(props){ 
    const id = this.props.id;
    this.props.history.push({
     pathname: '/downloadMusic',
    search: '?query=downloadMusic',
    state: { data: id }
    })

    return(
        
    <Styles>
    <Router>
    <Card style={{ width: '18rem'}} className="cardbg shadow" text="white">
      <Card.Img variant="top" src={props.image} className="img-fluid" />
      <Card.Body className="text-center">
        <Card.Title>آهنگ {props.name}</Card.Title>
        <Card.Text>خواننده {props.singer}
        </Card.Text>
        
        
        <Button  href="/DownloadMusic/:id" className="btn btn-outline-dark">go to download page</Button>
        
           
      </Card.Body>
    </Card> 
    </Router>
    </Styles>
    
);
}
export default SongCard;

And finally this is the last page which is downloadMusic. Here also there should be a card containing information about the specific song and the final download button.

import React from 'react';
import { songsList } from './songsList';
import DownloadCard from './DownloadCard';
import AudioPlayer from './Player/AudioPlayer';

function DownloadMusic() {
    const id = this.props.location.state.data;
     const song = songsList.find(item => item.id === id);

    return(
        <div>
        <DownloadCard className='col-md-6' id= {song.id} image={song.image} name={song.name} singer={song.singer}/><br/>
        <AudioPlayer/>

        </div>
        );
}

export default DownloadMusic;

What I want is to send the id and url of that specific song that was clicked in the Song page, to downloadMusic page and show it's information from songsList in a card. I've tried so many ways like using state, useParams(), ... but none of them worked for me. Also I don't want to send the whole props because I know it's not the best practice. So what am I doing wrong?

Hope I've shared enough information. any help is appreciated.

3 Answers

what you have to do is map the array of songs, like this

import React from 'react'
import React from 'react'
import SongCard from '../components/SongCard';
import { songsList } from '../components/songsList';

//this is first object of the songsList file which i hard coded instead of using api:

const songsList =[
{
    id : 1,
    url : './components/Player/Songs/Homayoun Shajarian - Souvashoun.mp3',
    image : require('../assets/Souvashoun.jfif').default,
    name : 'x',
    singer : 'y',
    style :'z',
    lyrics :'v',
    source : './Palyer/Songs/Homayoun Shajarian - Souvashoun.mp3'


}];

export default function Songs() {


    
    return(
    
        <div>
        {
           songsList.map((song, idx) => {

           return     <SongCard key={idx} className='col-md-6' id={song.id} image={song.image} name={song.name} singer={song.singer}/>
            })
        
        }    
        </div>
        
        );
}

You can create a function like this in the song card page

 const onSongCardClick = () => {
        history.push({
            pathname: '/downloadMusic',
              state: {id: this.props.id, url: this.props.url} 
          })
    }
...
...
<Button onclick="onSongCardClick()" className="btn btn-outline-dark">go to download page</Button>

And get those values in Download music page by

 const { id, url } = this.props.location.state

Also pass url as props to song card from songs page

Alright I finally found a way to do this, after a long effort! I'm gonna share it here if someone needed.

So in the SongCard.js I used useHistory() and a navigate function to save the id and navigate to downloadMusic page also in the onClick attribute of button used navigate function:

import React from 'react'
import {Card, Button} from 'react-bootstrap';
import styled from 'styled-components';
import {BrowserRouter as Router, useHistory} from 'react-router-dom';



function SongCard(props){ 
    //navigating and saving id
    const history = useHistory()

    const navigate = (id) => {
        history.push(`/downloadMusic/${id}`)
    }

    return(
        
    <Styles>
    <Router>
    <Card style={{ width: '18rem'}} className="cardbg shadow" text="white">
      <Card.Img variant="top" src={props.image} className="img-fluid" />
      <Card.Body className="text-center">
        <Card.Title>آهنگ {props.name}</Card.Title>
        <Card.Text>خواننده {props.singer}
        </Card.Text>
        
        //make sure you add onClick attribute!
        <Button  onClick={() => navigate(props.id)} className="btn btn-outline-dark">download</Button>

      </Card.Body>
    </Card> 
    </Router>
    </Styles>
    
);
}
export default SongCard;

Then in the DownloadMusic.js by using useRouteMatch() I accessed id param, this id was string so I converted it to number in the next step. At the end by using .find() method I found the specific song that was clicked for!

import React from 'react';
import { songsList } from './songsList';
import DownloadCard from './DownloadCard';
import AudioPlayer from './Player/AudioPlayer';
import { useRouteMatch } from "react-router-dom";


function DownloadMusic() {
    //getting id param
     const {
    params: { id },
  } = useRouteMatch('/downloadMusic/:id');
//converting the string id to number:
const num = Number(id);
//finding the song with song.id= num
 const song = songsList.find(item => item.id === num);



    return(
        <div>
    <DownloadCard className='col-md-6' 
        id= {song.id} 
        url= {song.url} 
        image={song.image} 
        name={song.name} 
        singer={song.singer}
        /><br/>
        <AudioPlayer id = {song.id}/>

        </div>
        );
}

export default DownloadMusic;

Just be careful about type of id, it must be number. this little one took me a long time. :) Good luck!

Related