React tricks for nested nightmares
Over the past few weeks learning React at my bootcamp, dealing with setting state and inverse data flow was challenging enough. But once I used a serializer so I could get all my data in one fetch, the setting the state of one key/value nested in another array gave me opportunity to be gentle with myself, practice patience and perseverance, get my hands dirty with conditional logic and nested twists and turns that would make M.C. Escher proud.
My co-conspirator and I created a Single Page Application with three Models. Users, Drinks, and Reviews as the joiner with both the foreign keys of user and drink id. This allow a simple .map() to render out the drinks and their corresponding reviews. Now to get the info from the backend. Have the drinks set to an empty array, so you can map over it before you even have the data. Then fetch the data in componentDidMount, and setState to what gets returned.
Now that we have that data in State, drill it down to the component that needs it. We imported a DrinkList, so we’ll send it down there
Once in DrinksList, we’re going to .map() of the drinks sent down in props. Now that they’re rendered, let’s add onClick to listen for which drink got clicked. Now begins the inverse data flow, so the OnClick triggers a function not yet created nor yet sent down called getDrink.. It takes an argument of the corresponding drink object. We’ll deal with that next.
Back up in the parent container, we create an arrow function (so we will not have to do any binding). The argument sent from down below was called drinks, but for ease of understanding, I call it gotDrink here. This this we set state of selectedDrink in state (line 10 in image all the way above). We now have to send the function down to the DrinkList.
Ok, now we got a selectedDrink in state. We will also have to drill it down to the DrinkPage. This way we can render the drink. However, before it’s set selectedDrink has a value of null, so down below we will have to conditionally render. If the selectedDrink exists, show it, if not return an empty <div>. We’ll also have full CRUD down there, so we have to drill down some info and a few functions we’ll build later.
If the selected drink exists, let’s start mapping out drink. However we can only delete and change our own reviews, not that of others. So if the review matches our id, render the CRUD buttons. We also have to set the comment up in state with an onChange, that passes back up the value. Otherwise, if it’s not our review, just write the review with no buttons or inputs.
Now another condition. If this drink has no reviews, or if I haven’t reviewed it, render an Add button. Set state above with the value. Drill the above state back down with props. Send the user up so we have the foreign key. Otherwise, do nothing. And finally on 44, if there was no selectedDrink, return an empty div.
Here comes the nested labyrinth. A wise man once explained the difference between a labyrinth and a maze to me. A maze has many twist and turns, but also has dead-ends from which you must back track. A labyrinth, while it has twists and turns, it has no dead-ends, and rather every turn leads you to your destination using your meandering journey for reflection and introspection. This ensuing logic was not a problem to solve, but a challenge to overcome. Mistakes were welcomed, for they added to my understanding. Let’s travel through this labyrinth.
I needed to fetch to the backend while also setting State to reflect on the front end. The back end reviews were nests as such.
Let’s deal with the most interesting one. A big and beefy patch fetch. The review was sent up, so RESTfully we need its id for the backend. Once we get the obj back, now with got to find it and change it in state. Create a variable updated Drinks that map over the drinks, which returns a new array of drinks the same size as the original ones in state. Map looking for an id that matches that object’s drink foreign key. If it does match, create new array called newReviews that maps over the review id. If it matches, replace the review with the returned object. If not, then just send back the review. Otherwise if the drink id didn’t match, send back the drink unaltered. Then setState with the updatedDrinks array. You have traversed this labyrinth.
Let’s check out how we handled a delete. Same dealie. Have to pass up the review for RESTful route in the backend, but another labyrinth for the front. Create a variable foundDrink that filters by the drink.id by review’s foreign key drink_id. Create another variable removeReviewArr which will be all the reviews for that drink minus the one you just deleted. And yet again, create a whole new copy newDrinks which maps over all the drinks, and if the drink id matches the found drink id, set it’s reviews to the removeReviewArr. Othewise it’s not the drink whose review we deleted, so return the drink untouched, reviews and all. Now the state changing drinks to newDrinks. Whew!
The after two or three lines of code (sometimes even one), I was console.log the info, or was checking the React Developer tools. It was not the speediest process. But at every stage I could return back the value of what I wanted, and if undefined or not what I expected, I could pause there fix the issue, rather than waiting and getting lost in the sauce.
a