Kevin Han Kevin Han - 3 months ago 13
React JSX Question

I think this is another React/JS context issue, how do I extract this variable?

I have an array[] of tracks that I receive from an API.
I pass it to a map function which will return a track for every track in tracks. I want to export a variable (Song) specific to that track to be be processed in my event handler as such. The only thing thats not working is the scope of song. I cant set the state of song in my map function or the component goes into an infinite rerender loop.

handleEnter(){
//I want to get the song into this context and play it here
this.props.mouseEnter();
}

handleLeave(){
//same for pausing
this.props.mouseLeave();
}

createTrack(track){
var song = new Audio([track.preview_url]);
return (
<div className="image" key={track.id}>
<img
className="img-circle"
src={track.album.images[0].url}
onMouseEnter={this.handleEnter.bind(this)}
onMouseLeave={this.handleLeave.bind(this)}
/>
<p className="showMe"><span>{track.name}</span></p>
</div>
);
}

getTracks(){
if(this.props.tracks) {
console.log(this.props.tracks);
return (
<div>{this.props.tracks.map(track => this.createTrack(track))}</div>
);
}
}

componentWillMount(){
this.props.fetchMessage();
}

render(){
return(
<div>{this.getTracks()}</div>
)
}

Answer

if you want to use .bind, you can send it to handleEnter and handleLeave.

handleEnter( trackID ) {
    // trackID available here
}

createTrack(track){
    var song = new Audio([track.preview_url]);
    return   ( 
        <div className="image" key={track.id}>
            <img
                className="img-circle"
                src={track.album.images[0].url}
                onMouseEnter={this.handleEnter.bind( this, track.id )}
                onMouseLeave={this.handleLeave.bind( this, track.id )}
            />
            <p className="showMe"><span>{track.name}</span></p>
        </div>
    );
}

It's typically best practice to not use .bind in react since it creates a new function on every render. Rather, you should create a <Track /> component, pass it the track, then pass handleEnter and handleLeave as props.

const track = ( props ) => {

    let { track, handleEnter, handleLeave } = props;

    const onMouseEnter = () {
        handleEnter( track.id );
    }

    const onMouseLeave = () {
        handleLeave( track.id );
    }

    return (
        <div className="image" key={track.id}>
            <img
                className="img-circle"
                src={track.album.images[0].url}
                onMouseEnter={ onMouseEnter }
                onMouseLeave={ onMouseLeave }
            />
            <p className="showMe">
                <span>{track.name}</span>
            </p>
        </div>
    );

};

then in your render, you'd map like you're doing and output <Track /> pure components instead of full-on components