ReactJS - How to assign an JSX element to a variable and use it in render method

ReactJS - How to assign an JSX element to a variable and use it in render method
0

#1

Hi Campers! Recently I’m working on this project of a music player and I have a Class Component “MuscicPlayer”, which has a render method like this:

render() {
        let nowPlayingSong = this.state.songList.getSongAtIndex(this.state.nowPlayingSongIndex);
        let nowPlayingSongURL = nowPlayingSong.songURL;
        let nowPlayingSongAlbumArt = nowPlayingSong.songAlbumArtLink;
        let nowPlayingSongTitle = nowPlayingSong.songTitle;
        let nowPlayingSongAuthor = nowPlayingSong.songAuthor;

        if (this.state.isShowingNowPlayingBoard === true)
            return (
                <div id="music-player">
                <SongInfo 
                    albumArt={nowPlayingSongAlbumArt}
                    songTitle={nowPlayingSongTitle}
                    songAuthor={nowPlayingSongAuthor}
                    currentTime={this.convertSecondToMinute(this.state.currentTime)}
                    duration={this.convertSecondToMinute(this.state.nowPlayingSongDuration)} />;
                <audio 
                    src={nowPlayingSongURL} 
                    id="audio">
                </audio>
                <Scrubber percent={this.state.percent} />
                <ControlPanel 
                    handlePlayPause={this.handlePlayPause}
                    handleNext={this.handleNext}
                    handlePrevious={this.handlePrevious}
                    handleShuffle={this.handleShuffle}
                    handleToggleNowPlayingBoard={this.handleToggleNowPlayingBoard} />
                <NowPlayingBoard 
                    songList={this.state.songList.songList} 
                    toggleShowing={this.handleToggleNowPlayingBoard}
                    removeSong={this.handleRemoveSong}
                    isPause={this.state.isPause} />
            </div>
            );
        else return (
            <div id="music-player">
                <SongInfo 
                    albumArt={nowPlayingSongAlbumArt}
                    songTitle={nowPlayingSongTitle}
                    songAuthor={nowPlayingSongAuthor}
                    currentTime={this.convertSecondToMinute(this.state.currentTime)}
                    duration={this.convertSecondToMinute(this.state.nowPlayingSongDuration)} />
                <audio 
                    src={nowPlayingSongURL} 
                    id="audio">
                </audio>
                <Scrubber percent={this.state.percent} />
                <ControlPanel 
                    handlePlayPause={this.handlePlayPause}
                    handleNext={this.handleNext}
                    handlePrevious={this.handlePrevious}
                    handleShuffle={this.handleShuffle}
                    handleToggleNowPlayingBoard={this.handleToggleNowPlayingBoard}
                    isPause={this.state.isPause} />
            </div>
        );
    }

As you can see, in the render method, I have an if-else statement and inside I have some functional components. But the problem is the duplication in my code, I have tried assign the duplicated components to variables before the return statement and reuse them inside the return but It just doesn’t work, this is how I tried:

render() {
        let nowPlayingSong = this.state.songList.getSongAtIndex(this.state.nowPlayingSongIndex);
        let nowPlayingSongURL = nowPlayingSong.songURL;
        let nowPlayingSongAlbumArt = nowPlayingSong.songAlbumArtLink;
        let nowPlayingSongTitle = nowPlayingSong.songTitle;
        let nowPlayingSongAuthor = nowPlayingSong.songAuthor;
        **let songInfo = <SongInfo **
**                    albumArt={nowPlayingSongAlbumArt}**
**                    songTitle={nowPlayingSongTitle}**
**                    songAuthor={nowPlayingSongAuthor}**
**                    currentTime={this.convertSecondToMinute(this.state.currentTime)}**
**                    duration={this.convertSecondToMinute(this.state.nowPlayingSongDuration)} />;**
**        let controlPanel =  <ControlPanel **
**                    handlePlayPause={this.handlePlayPause}**
**                    handleNext={this.handleNext}**
**                    handlePrevious={this.handlePrevious}**
**                    handleShuffle={this.handleShuffle}**
**                    handleToggleNowPlayingBoard={this.handleToggleNowPlayingBoard} />;**

        if (this.state.isShowingNowPlayingBoard === true)
            return (
                <div id="music-player">
                **{songInfo}**
                <audio 
                    src={nowPlayingSongURL} 
                    id="audio">
                </audio>
                <Scrubber percent={this.state.percent} />
                **{controlPanel}**
                <NowPlayingBoard 
                    songList={this.state.songList.songList} 
                    toggleShowing={this.handleToggleNowPlayingBoard}
                    removeSong={this.handleRemoveSong}
                    isPause={this.state.isPause} />
            </div>
            );
        else return (
            <div id="music-player">
                **{songInfo}**
                <audio 
                    src={nowPlayingSongURL} 
                    id="audio">
                </audio>
                <Scrubber percent={this.state.percent} />
                **{controlPanel}**
            </div>
        );
    }

Could you show me the way to refactor my code so that it would be less duplicated? Thank you very much?


#2

I believe you should review this React challenge, which shows you how to do what you want.


#3

To refactor I would suggest having just one return block and have conditionals only on the things that are different. If I read it correctly it seems like the big difference is is the NowPlayingBoard? Maybe just have that set inside an inline conditional like the example randel linked above.

Another thing that could possibly be improved in a refactor might be breaking the song information down in a lower level, and passing down less props at this level.


#4

Adding to @Lucasl’s suggestion, I would try to make your code more readable by putting the following in your render method above the return statement:

const nowPlayingSong = this.state.songList.getSongAtIndex(this.state.nowPlayingSongIndex);
const {songAlbumArtLink, songTitle, songAuthor} = nowPlayingSong;
const nowPlayingSongInfo = {
  songAlbumArtLink,
  songTitle,
  songAuthor,
  currentTime: this.convertSecondToMinute(this.state.currentTime),
  duration: this.convertSecondToMinute(this.state.nowPlayingSongDuration)
};

Then inside your return statement you would pass a prop named info which is assigned nowPlayingSongInfo. Then ini your SongInfo component, you would receive an object with the property names songAlbumArtLink, songTitle, songAuthor, currentTime, and duration which you could easily destructure and apply in that component. Note that I used the same object property names that you were using in the nowPlayingSong object, so you might need to adjust your SongInfo component to use these different property names.

Overall, it definitely simplifies your code, because it takes advantage of object desctructuring all around.

FYI - Your code could really use some shorter names for variables. They seem a bit too long and actually makes it harder to read in my opinion.


#5

adding my voice to @Lucasl and @RandellDawson you should benefit from es6 syntax to write and read your code easily and use a simpler variable names , you also could use what is like jsx variable in your component and use it in you render method but I suggest using @Lucasl solution in this example but if you want to do it in other components when needed

class ReactCompnent extends Component {

renderSomeJSX() {
return <Jsx />;
}

render() {
 retrun (
    <AnotherJsx />
     {//use the method as a variable be calling it}
     {this.renderSomeJSX()}
 );
}

}