Udacity React Nanodegree: Project One

TLDR

Read the directions.

It’s been about two week’s since I started Udacity’s React Nanodegree course. A large part of it is breaking my habitual ways of thinking. Embrace the new.

Is there a good tool that builds a diagram of component-based frameworks?

WHEN IT COMES TOGETHER

I love the feeling when you’re coding and it comes together. It’s the small victories that add up to satisfaction. These modern frameworks like React are good for a little instant gratification. Declarative programming and data binding equals pieces falling into place like Tetris.

The only thing that will consume more of your time than frustrated, banging your head against the table, learning curve, I’m never going to finish this in time coding, is the smooth sailing, getting a lot of stuff done, in a groove and don’t want to stop, time disappears, making magic coding.

WHEN IT DOESN’T

Of course, there are frustrations. When learning new frameworks you end up using whatever sources of info are available: YouTube, blogs, articles, code repos, and forums.

The project seemed straightforward at first. I wanted to be done early. I got the basics working. Then I read the requirements again a few days later. Oh. Right. The things I had questions about were intentionally designed that way. Mental note: RTFD.

There are two pain points that slow me down:

  1. Not thinking in components (declarative vs. imperative)
  2. Outdated information online

A New Way of Thinking

I’m still learning the new ways. When I hit a road block, that road block is often me. A trivial example: I want to update a SELECT so it shows what value is selected. My instinct is still to grab it with a selector, like in the jQuery days, and do what needs to be done: add a selected attribute, change the color of an adjacent DIV’s text, etc.

But that is an anti-pattern. Here’s what I should be doing:

Make the SELECT its own component and give it a state. Update the state, bind the appropriate properties in JSX to the state properties, and Bob’s your uncle.

 


// This is what I want to do.
// Assuming that the <select> has an id.
$('select#assignShelf').change(function() {
$(book).css({ border: 'solid 2px green', fontWeight: 'bold' }).text('Assigned to shelf')
// or
$(book).addClass('selected') // to be consistent with the example below
})
// But React uses a top down approach.
// I don't tell the book how to change its display when an option is selected.
// When an option is selected, I change the state.
// Change the state, and the component re-renders, template strings are populated,
// classes are toggled, styles are invoked, and all is right in the world.
class Book extends Component {
state = {
shelf: "None",
isChanging: false
}
handleChange = (event) => {
this.setState({
shelf: event.target.value,
isChanging: true // When the state changes, the component re-renders.
})
}
return {
<div className="book" {isChanging && className="selected"}>
<div className="book-cover" /> {/* background image */}
<div className="title">{book.title}</div>
{isChanged && <div className="shelf">{book.shelf}</div>}
<select value={this.state.shelf} onChange={this.handleChange}>
<option value="Currently Read">Currently Read</option>
<option value="Want to Read">Want to Read</option>
<option value="Read">Read</option>
<option value="None">None</option>
</select>
</div>
}
}

It flows. Go with the flow.

Declarative Over Imperative

This isn’t React-specific. It’s more about the style and approach of modern JavaScripting. I prefer praxis over theory. Here’s the long and short of it. In this example, use functional programming friendly methods instead of for loops. (I don’t see a way to only embed certain lines of a “gist”, so here’s the whole exercise.)


const shelvedBooks = [
{
id: 1,
title: "Eggs: The Whole Truth",
shelf: "currentlyReading",
authors: ["George Gandy"]
},
{
id: 2,
title: "How to Play the Kazoo",
shelf: "read",
authors: ["Pete Sneakers"]
},
{
id: 3,
title: "I Married Bigfoot",
shelf: "wantToRead",
authors: ["Darby Trent"]
}
];
const searchResults = [
{
id: 1,
title: "Eggs: The Whole Truth",
authors: ["George Gandy"]
},
{
id: 2,
title: "How to Play the Kazoo",
authors: ["Pete Sneakers"]
},
{
id: 3,
title: "I Married Bigfoot",
authors: ["Darby Trent"]
},
{
id: 4,
title: "The Straight Dope",
authors: ["Audrey Bo Baudrey"]
},
{
id: 5,
title: "The Paperclip Condundrum",
authors: ["Mr. Whiskers"]
},
{
id: 6,
title: "Cereal Killers",
authors: ["Captain Crunch"]
},
{
id: 7,
title: "Have a Good Day",
authors: ["Smohn Jith"]
},
{
id: 8,
title: "Frank and Earnest",
authors: ["Earnest Frank"]
}
];

view raw

books.js

hosted with ❤ by GitHub


import books.js
// Here's an example of "outdated" ways in favor of the new ways
// In our project, we're working with an API.
// GetBooks returns all the books that are assigned to shelves and each book has a shelf property.
// SearchBooks returns books from a search, BUT the search results have no shelf property.
// I need to go through the search results books and add the shelf property (or "none" if the book doesn't have a shelf)
// #1
// for loops
// In the past I would have used "for" loops, of course.
// In this code, I'm not being careful about changing the searchResults array; I'm adding a shelf property TO THE EXISTING ARRAY.
// Nested for loops? Not the greatest.
// I could add some variables in here to make it more reader-friendly, too.
let searchResultsWithShelves = [];
for (let i = 0; i < searchResults.length; i++) {
searchResults[i]["shelf"] = "none"; // Adding a default shelf property
// Find a match in shelvedBooks
for (let j = 0; j < shelvedBooks.length; j++) {
if (searchResults[i].id === shelvedBooks[j].id) {
// Add shelf property to the search results book
searchResults[i].shelf = shelvedBooks[j].shelf;
}
}
searchResultsWithShelves.push(searchResults[i]);
}
// #2
// REDUCE
// Use reduce to assign shelf property to each book in search results
const reduced = searchResults.reduce((accum, foundBook) => {
const match = shelvedBooks.find(book => book.id === foundBook.id);
foundBook["shelf"] = match ? match.shelf : "none"; // <- Still changing an input. No bueno.
return […accum, foundBook];
}, []); //?
// #3
// MAP
// I'm not really reducing anything, so map will do.
// Making a copy of foundBook instead of mutating the input.
// Also, I'm using a ternary instead of if-then-else. That's worth style points now.
const mapped = searchResults.map(foundBook => {
const match = shelvedBooks.find(
bookOnAShelf => bookOnAShelf.id === foundBook.id
);
return match
? Object.assign({}, match, {
shelf: match ? match.shelf : "none"
})
: foundBook;
}); //?

Research and Troubleshooting

The web development landscape is changing constantly. Libraries and frameworks are updated and changed/improved frequently, not to mention browsers (although browsers tend to be very good at not breaking old things as they support new things).

This means that by the time a language specific coding book would published and released, it’s already out of date.

If you have questions, forums like StackOverflow and Reddit will have answers, but there’s a good chance they’ll be out of date, so be wary. I had the same issue once when there was a “fire drill” at work that involved Angular 4, at the time. It was so new that everyone was still getting caught up, including the official documentation.

Example: I want to show a loading spinner while the book info is being fetched. A generalized function for that behavior would be nice, huh. Code it once, sprinkle it around where needed.

Long story short, it looks like Render Props are the way to go. Higher Order Components out; Render Props in.

THE BIG PICTURE?

I’m a visual person. A picture is worth a thousand words, as they say. Component-based web development is prime territory for visualization tools and utilities.

Is there a way I can generate a diagram of an app? Obviously, these kinds of visualizations can get chaotic for large projects, but that sounds like an opportunity to me.

WHAT’S NEXT

I submitted the bookshelf project. Aside from the fact that there were a few components I should have made stateless, it was good. And I got some advice on how to deal with a few things: global constants, throttling/debouncing.

Of course, I’m a little compulsive so I did a few fixes based on the feedback, and it’s still sticking in my craw that I didn’t get those loading spinners working.

For now, moving on to part two: Redux.

Level up.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.