Direction Home
Tags: react, beginners, music
Published: 2022-01-05 19:27:53

Lost? My React application "Direction Home" can help you find your way home... But only if you're lost in a metaphorical sense, kind of like a rolling stone...

Yes, my app is for Dylan fans. And yes, the lyric drops are just going to keep on keepin' on even as the times are a changin'.

The app is a single-page application and contains only one .html file. The parent "App" component utilizes Route and Switch hooks from React Router to create four distinct client-side routing pages - "Home", "Rank Songs", "Live", and "Moments":

```javascript
function App() {

  return (
    <div>
      <NavBar />
      <Switch>
        <Route exact path = "/rank-songs">
          <RankSongs />
        </Route>
        <Route exact path ="/live">
          <Live />
        </Route>
        <Route exact path ="/moments">
          <Moments />
        </Route>
        <Route exact path="/">
          <Home />
        </Route>
      </Switch>
    </div>
  );
}
```
The "Home" component serves as the homepage of the application with two rows of Dylan album covers as decoration. It contains a "LyricWindow" component that displays a Dylan lyric from a curated array utilizing useState and useEffect React hooks.

More on that...

I created a randomLyric function that takes in an array as an argument, in our case that will be the hard-coded lyric array.

The function uses the built-in JavaScript methods Math.floor and Math.random (times the length of the array) to generate a random number that will serve as the index of our random lyric, which is stored in the variable "random". If the value of "random" is **NOT** exactly equal to the value of "lyric" stored in state, then the function uses the lyric setter function setLyric to set the state variable lyric to the value of "random."

If, however, "random" does indeed equal the value of "lyric" - that is, the randomly generated lyric matches the lyric stored in state and already displayed in the lyric window, then our "else" conditional executes, recursively calling itself on the passed in array. In other words, we repeat the process from the beginning until the "random" lyric does not match the current "lyric" in state:
```javascript
function randomLyric(array) {
    let random = array[Math.floor(Math.random()*array.length)];
    if (lyric !== random) {
         setLyric(random);
    } else {
        randomLyric(array)
    }
}
```
Finally, useEffect is called and passed the randomLyric function with lyricArray as an argument. Now, when the LyricWindow component renders, lyricArray will be invoked as a side effect. Moreover, as useEffect's second argument is an empty array, it will only run the first time the component is rendered:
```javascript
useEffect(() => randomLyric(lyricArray), []);
```
As for the ever-changing lyric, that is handled by a separate call of useEffect that takes in setTimeout as a callback function and calls the randomLyric function every 5 seconds (5000 milliseconds).
```javascript
useEffect(() => {
    setTimeout(() => randomLyric(lyricArray), 5000)
})
```

The "RankSongs" component incorporates user interactivity, allowing users to use "up" and "down" arrows to rank a list of 10 Dylan songs (if you've never taken to Bob Dylan's music, these aren't a bad place to start). I created a function to handle the onClick for both buttons:
```javascript
function onVote(e){
        const voteClass = e.target.className;
        const votedSong = e.target.parentElement.parentElement.id;
        const currentIndex = rankedSongs.indexOf(votedSong);
        const updatedSongs = [...rankedSongs];

        if (voteClass === "up") {
            if (currentIndex === 0) {
                return updatedSongs;
            } else {
            [updatedSongs[currentIndex], updatedSongs[currentIndex - 1]] = [updatedSongs[currentIndex - 1], updatedSongs[currentIndex]];
            }
        } else {
            if (currentIndex === 9) {
                return updatedSongs;
            } else {
                [updatedSongs[currentIndex], updatedSongs[currentIndex + 1]] = [updatedSongs[currentIndex + 1], updatedSongs[currentIndex]];
            }
        }

        setRankedSongs(updatedSongs);
    }
```
The changes will be persisted if the user saves their list.

The "Live" component contains a grid of live Dylan performances, including his "duel" with Donovan in D.A. Pennebaker's Dont Look Back (sic). 

As for "Moments", the user can share a Dylan moment from their own life and it will display in a window with other fan moments, all of which are persisted.

Thank you for reading.

Glad to see you're still alive, you're looking like a saint!