Vincent La

Building my First React App: Rants and Raves

January 9, 2020

I was looking for a side project to give my portfolio a nice kick. I have a lot of data science projects from my school days, and plenty of C++ projects, but I had nothing that dealt with the front end of the web. I also needed a new resume, so I figured why not make a resume builder? About a month later, I finally had a functional web app.

Why I Don't Like (Heavyweight) Frameworks

There's been plenty of hate given to CSS frameworks already, but after my own unpleasurable experience, I couldn't help but to join in the fray. I added "heavyweight" in the title because I ended up building on top of Pure CSS, a minimal CSS framework that for the most part has helped me when I needed it and gotten out of the way when I wanted needed customization.

Everything Looks the Same

Throughout the internet, developers lament that all websites designed with Bootstrap look like websites designed with Bootstrap. Of course, you could always override the provided classes and define your own styles, which leads me to my next point.

What Value Do They Add?

Heavyweight frameworks like Bootstrap and Material UI heavily influence the way you think and shape your app by providing certain components and classes. Of course, you can override the provided styles and juxtapose different components together after spending hours reading the documentation to learn the API, what CSS classes are exposed, figuring out what your want your final website to look like. Then, with this knowledge, you can safely perform the surgical procedure of overriding the right styles. Once you're done with that, for every component you wanted which the library didn't provide, you get to supply your own components as well as figuring out how you'll style them to maintain a consistent look and feel throughout your website.

On the other hand, if you have that much knowledge of CSS, why don't you just create your own mini-framework for your own site? That way, you get to style your website any way you like without the trouble overriding a bunch of crap that somebody else wrote.

I do not understand the popularity of frameworks like Bootstrap—they seem like a bad meme that just won't die. Sure, using Bootstrap will give you an attractive website out of the box, which makes it great for designing admin sites. But trying to make a serious project with a CSS framework is like trying to write a book with half of the sentences already filled out. After all of the deleting and trying to figure out how your original thoughts and creativity are going to fit with the material, doesn't it just make sense to start from scratch?

Why I Don't Like Bootstrap

Pointless Utility Classes

It seems kind of pointless to complain about the utility classes, but then again, so are the utility classes. I don't understand why a whole library of stuff like

.float-right {
    float: right !important;
}

needs to exist. I understand writing CSS can be hard, but is setting properties so hard that we need to abstract that away? Even worse, classes like .float-right defeat many of the reasons for using CSS. If you style something with class='float-right', it can only ever float to the right. You can't have it do something different based on a responsive media query. Furthermore, if you decide you want something to not float right, you'll have to edit the class out of your HTML, which means that your HTML and CSS are coupled together.

Moronic Print Stylesheets

I'm not the first person to encounter issues with printing pages that were styled with Bootstrap, and I'm sure I won't be the last.

My problems began when I created a print-friendly view for my resume generator. All this view contained was the resume itself, which did not have any Bootstrap components or classes. However, when I used Firefox's print preview, I noticed that the font was really small and the content would sometimes overflow despite me setting width: 100% on the topmost container. I thought I was going crazy, until I used an earlier HTML resume I made with vanilla CSS. Once I realized what was going on, I deleted the Bootstrap print stylesheet and everything was fine again (eventually I would delete all mentions of Bootstrap from my code).

Apparently, the Bootstrap print stylesheet is heavily opinionated and has a ton of styling for various breakpoints. This led to people coding lots of various CSS overrides in the Stack Overflow post above.

Even worse, the concept of a responsive print stylesheet is totally inane. Responsive print stylesheet? What about an 8.5 by 11 inch sheet of paper screams "responsive" to you? If I wanted my resume to print on a sheet of A4-sized paper or a 3 x 5 index card, I could easily handle the styling myself. I don't need a deluge of useless CSS rules that I'll have to manually override anyways.

Material UI Woes

After Bootstrap I decided to use another UI library called Material UI, which is called that because it is supposedly designed according to Google's Material Design Guidelines (don't ask me to describe what that means). I thought with more than 50k GitHub stars, you can't beat the wisdom of the crowd. That's when I learned the price of blind conformity.

Who thought this API was a good idea?

Material UI comes out of the box looking like every other Android app. This is fine for a lot of purposes, but I was looking to give my app a unique identity. That's when I discovered Material UI's CSS in JS solution which allows you to embed styling inside your React app, thereby totally defeating the separation of concerns CSS allows you to achieve.

const useStyles = makeStyles({
  root: {
    color: 'red',
    '& p': {
      color: 'green',
      '& span': {
        color: 'blue'
      }
    }
  },
});

If you were using SASS, the equivalent would be

.some-class {
    color: red;

    p {
        color: green;

        span {
            color: blue;
        }
    }
}

Although most frameworks claim to help you build websites faster, that's only true if you settle for a cookie cutter appearance. The makeStyles() API requires you both understand how CSS works as well as an arcane JavaScript syntax. If you want custom styling from the Material UI library, you'll have to pay for it through countless hours wading through the documentation. Even worse, it has its own style specificity rules. If fighting with the "cascading" part of CSS is too boring and easy for you, then you can add another layer of abstraction to your style soup.

Why Use CSS in JS? Why not just regular CSS/SASS?

First of all, multiple people have written great arguments against CSS in JS.

Personally, here are my reasons for sticking with the tried and true solution of SASS (I'll be using CSS and SASS somewhat interchangably since the learning curve for going from knowing CSS to knowing SASS is probably shorter than your morning waking routine.):

Yes, I know the list above isn't long, but shouldn't we be trying to make software simpler and our lives easier? If you're proposing that we add another layer of abstraction of top of something that already exists, the burden of proof lies on you to that it actually provides enough value to offset the costs of learning it and the costs of additional complexity. So far, I'm not sold on CSS in JS solutions.

TypeScript

I wasn't always a fan of statically typed languages, but after fighting through a slew of type errors and similar bugs in Python and R, I eventually switched sides. Although there's a decent-sized upfront learning cost to using TypeScript if you're not familar with statically typed languages, I believe the cost is well worth it in the long run. On the other hand, it you're used to using statically typed object-oriented languages with generics, especially C# (the architect behind TypeScript and C# are the same guy), then TypeScript will be well within your comfort zone.

"But I don't need types"

One common argument against type systems is that developers simply don't need types. The problem with this argument is that all code is typed. What?!? That's right, I said it. All code is typed, either explictly or implicitly.

For example, take a look at the code below:

function Event(props) {
    let date = <span>Date: No date set.</span>
    if (props.date) { 
        date = <span>Date: {props.date}</span>
    }

    let location;
    if (Array.isArray(props.location)) {
        location = <span>Locations: {props.location.map(loc => <span>{loc}</span>)}</span>
    }
    else {
        location = <span>Location: {props.location}</span>
    }

    return <div>
        {date}
        {location}
    </div>
}

If you look at the code above, we're treating props as if:

This code, although not explicitly typed, will only take in props of the above type unless a runtime error is the desired state of your program. If you say your code doesn't have types, you're just lying to yourself. Furthermore, the code above assumes location is always defined—which works well... until it isn't.

On the other hand, with some simple TypeScript annotations, we can easily add some compiler-assisted safety to our code.

interface EventProps{
    date?: string;
    location: string | string[];
}

function Event(props: EventProps) {
    let date = <span>Date: No date set.</span>
    if (props.date) { 
        date = <span>Date: {props.date}</span>
    }

    let location = <></>;
    if (Array.isArray(props.location)) {
        location = <span>Locations: {props.location.map(loc => <span>{loc}</span>)}</span>
    }
    else {
        location = <span>Location: {props.location}</span>
    }

    return <div>
        {date}
        {location}
    </div>
}

In the example above, we barely modified our function. All we did was add a type annotation to props and define the interface we expected props to implement. Furthermore, because we didn't add a ? next to location, the compiler will automatically complain every time we pass in a possibly undefined value to location, therefore making our lack of undefined checks in Event() much safer.

Another thing that programmers of other languages might noticed is that we still didn't declare the types of date and location. This is because TypeScript infers their type from their usage, just like auto in C++ and var in Java.

Minor Annoyances

TypeScript isn't perfect, and there were plently of minor annoyances I had the displeasure of dealing with.

Tracking Down Types for JavaScript Packages

Many popular JavaScript libraries, including React itself, are just that – JavaScript libaries. This isn't necessarily a problem per se since TypeScript allows you to treat untyped JavaScript code implictly as any, but using a large amount of untyped JavaScript code defeats the point of using TypeScript. Luckily, there are actively maintained, high-quality type definitions for React and other popular JavaScript libraries. However, this isn't the case for every library.

Not Being Able to Use Type Aliases When Calling new()

One of my favorite features of C++ is being able to declare type aliases for just about anything. For example, if you have a ridiculously complicated type like std::unordered_map<MyKeyType, std::vector<MyValueType>> then you could simply declare MyMap = std::unordered_map<MyKeyType, std::vector<MyValueType>> and reuse the name MyMap everywhere instead of typing out the monostrosity that it represents.

Like C++, TypeScript also has type aliases. However, unlike C++, you can't use them when calling new. Now, I understand that the TypeScript design goals explictly state that copying the features of other popular languages is not a goal, it baffles me why this isn't possible.

Personally, there's two main reasons to use type aliases:

  1. To explictly describe the intent behind a type, e.g. ShoppingList is much more descriptive than Array<string>
  2. To reduce the number of keystrokes

Not allowing type aliases to be used when calling new defeats both of these reasons. It seems to be it would be easy to implement, since you'd just replace every instance of TypeAlias with ReallyLongTypeThatIsAliasedByTypeAlias.

But then again, I didn't write the TypeScript compiler.

React is Amazing

Before React existed, I resisted getting involved in front-end development because of the revolving door of new JavaScript frameworks. In the 2000s, due to CSS' lack of power, you had lots of vanilla JavaScript code performing basic functions such as highlighting links on hover. Often, a poorly made script would slow your browser down to a crawl or crash it entirely. Then, jQuery took over the scene and Google jumped in the fray with their Angular framework.

I haven't gotten the experience of working with Angular or jQuery, but I do have experience using MVC libraries in other languages like Django, Ruby on Rails, and Windows Presentation Forms. I also have the painful experience of trying to create snippets and apps in plain JavaScript and manipulating the DOM directly using the document.createElement() API.

There is a lot to like about React even though, unlike Angular, it only handles the UI aspect of web development. Sometimes less is more, and being empowered to choose between just using localStorage or a full blown database is nice to have. Furthermore, the declarative nature of React makes web development actually fun. Instead of having to keep track of different DOM nodes and remembering to delete them, React does all of that for you. As a result, I find then when I'm programming using React, the majority of the focus is on the data and how my app manipulates that data. After all, isn't that what most software is written to accomplish anyways? To perform a desired set of behaviors given certain input data?

</BlogPost>

Building my resume builder has been a fun experience, although frustrating at times. I don't expect anybody to take my words as gospel. But, in a world filled with a dizzying array of choices, I hope that my article helps the next stuck developer make better choices. Lastly, I hope this article motivates at least one developer to try to learning CSS instead of relying on frameworks as a crutch.