Let the Music Render: a sample app demonstrating React component hierarchy & interaction — Part 1 of 4
When I was first learning React, I was impressed by the power and speed of the framework, but it took time for me to really grasp how the different components worked together. In particular, I struggled to understand how a single React component could have different behavior depending on its container.
In order to illustrate how reusable components work together in React, I have created a sample app for a music player that lets users create their own playlists. Over the course of three posts, I will use this app to demonstrate how to:
- create a nested hierarchy of React components
- initialize and set state
- pass down data and functions to child components as props
- transform an array of data into a collection of components
- create reusable components that will behave differently depending on their container
Let’s say I want to create a music app dedicated to the hit songs of the eighties. I want my app to have two areas or “containers” — a catalog of all the songs at the top, and then an area underneath for the user to create their own playlist. When a user clicks on any song in the top container, it should be added to their playlist at the bottom. If the user clicks on the song in the pink playlist section, it is removed from the playlist.
Because I am using React, I know that my app needs to be built from components — independent and reusable pieces of code that each return one HTML element. Components in a React app are arranged in a nested hierarchy, in this case:
At the top of the hierarchy is the MusicPlayer component; all the other components of my app will be nested within MusicPlayer. Next, my two “container” components — the song catalog and the user’s playlist — which sit next to each other in the hierarchy as “siblings”. At the bottom I have the SongCard component that I will use to display each song, with the image, title, artist, and year. Both SongCatalog and YourPlaylist display SongCards.
To build out my app, I will start at the top of the hierarchy. In React, data always flows down from a “parent” component to its “children”. Since MusicPlayer is my top-level component, it is the only one that is capable of providing data to every other component in my app. This makes it a perfect place to store the state of my application.
In React applications, state is an object where we store property values that belong to a specific component. Anytime the state data changes, React “reacts” by re-rendering the component. Essentially, when we set state for a component, we are telling React “watch this object, and if any of its values ever change, re-render the component”.
In the case of my MusicPlayer, I need to assign two pieces of state: an array which will hold all of the songs in my song catalog, and another array that will hold all of the songs in the user’s playlist. Inside the class definition for the MusicPlayer component, I initialize the state by creating both properties and assigning them to blank arrays.
Next, I need to populate my allSongs array by making a fetch request to my backend database that holds the song catalog. I want to make sure this happens at a specific point in the React “lifecycle” — after I have set the state, but before my app is available for user interaction.
Any time I need to change a piece of state, I use the setState function, because React will only “react” to changes in state that happen through that function. The above function will fetch all the songs from the collection and assign them to the allSongs array in state.
In Part Two, I will add:
- a function to add a song from the catalog to the user’s playlist
- a function to remove a song from the user’s playlist
- the render function for the music player