JavaScript Habits That Grind My Gears
Tags: javascript, webdev
Published: 2023-10-16 10:28:55

Ah, JavaScript. The language that powers the web has its quirks. While it has evolved significantly over the years, there are still some practices that make me scratch my head. Let's dive into some of the coding patterns that truly grind my gears.

## 1. Overusing Ternary for Conditional Rendering in React

The ternary operator is concise and can be a delight to use, that's why many languages introduced it. However, in React, I've noticed a trend that goes against the grain of most programming style guides. Consider this code:

```javascript
{contacts.length
  ? contacts.map(contact => (
      <li key={contact.id}>
        {contact.firstName} {contact.lastName}
      </li>
    ))
  : null}
```

There are 3 reasons I'm not too fond of this approach, even though some React devs endorse it:

- It hinges on JavaScript's truthy/falsy definitions.
- The alternative path is empty, making the ternary redundant.
- The purpose of a ternary is to grasp the logic quickly, but multi-line format makes this harder.

Instead, I'd advocate for a more direct approach:

```javascript
{contacts?.length > 0 && contacts.map(contact => (
  <li key={contact.id}>
    {contact.firstName} {contact.lastName}
  </li>
))}
```

I know that it might be a controversial one, as there are quite a few posts that advocate using ternary.

## 2. Not Using Optional Chaining

Thanks to @ingosteinke for pointing this out! For some weird reason though it is a TS feature 🤦

As you saw in the previous example, we were testing something like this:

```javascript
contacts.length > 0
```

It might be a problem if contacts are `null` for example. We can prevent it by writing:
```javascript
contacts && contacts.length > 0
```

But this can be refactored with the optional chaining operator (?.), which checks if the object accessed or function called is null or undefined. If that's the case it returns `undefined` instead of throwing an error. So this condition could be changed to:
```javascript
contacts?.length > 0
```

> Note: even though optional chaining works with `undefined`, it won't work with a non-declared root objects.

## 3. The Reliance on `for` Loop for Array Iteration

For those who began their coding journey with C/C++, `for` loops are foundational. But when it comes to iterating over arrays in JavaScript, they're often overkill. Take this real-world ES5 code:

```javascript
var values = [];                    
var keys = Object.keys(elemObj);

for (var i = 0; i < keys.length; i++) {
    var val = parseInt(elemObj[keys[i]], 10);
    values.push(val);                
}        
```

This can be elegantly refactored to:

```javascript
const values = Object.keys(elemObj).map((key) => parseInt(key, 10));
```

I would argue that `for` loops are rarely needed in JavaScript applications.

## 4. Improper Iteration

A close cousin to the previous point, I've seen developers use `forEach` for value transformation:

```javascript
const newArr = []
arr.forEach((el) => newArr.push(sometransformation(el)));
```

For such tasks, `map` is the better tool. Use `forEach` when you are concentrated rather on side effects, not simple transformations.

## 5. Overlooking the "Standard Library"

It's essential to be familiar with the built-in methods of a language. As a JavaScript developer you should know methods for basic types - like array, string, date or object. For instance, checking if an array contains an element:

```javascript
if(arr.indexOf(el) != -1) { … }
```

Could be simplified to, as of ES2015 if I'm not mistaken:

```javascript
if(arr.includes(el)) { … }
```

And let's not forget about other array methods like `some` and `every`.

## 6. Neglecting String Interpolation

For many years we built our string that included variables like this:

```javascript
var str = "Hi, my name is " + name + " and I'm a " + occupation + ".";
```

ES6 introduced a beautiful feature for constructing strings, called [template literals](https://devprogrammer.com/programming/javascript-template-literals):

```javascript
const str = `Hi, my name is ${name} and I'm a ${occupation}.`;
```

Much better and cleaner. While most people don't write new code using outdated methods, there's truly no reason to retain old practices in any codebase. Especially when tools like ESLint or Prettier can rectify such issues with a single command.

## 7. Importing Bulky Libs to Hastily

If Lighthouse reports that you have a lot of unused JavaScript, you might want to address it. Some frontend developers add dependencies to a project as if there's no tomorrow. While sometimes it's the best approach, adding dependencies to a frontend comes with a cost, as this code eventually becomes part of a bundle sent to the client.

Two of the biggest offenders might be Moment and lodash.

Moment is hefty, weighing in at nearly 80kB gzipped! I understand that the Date type in JS leaves much to be desired, but is 80kB really necessary just to display a date?

Lodash, on the other hand, is often used to simplify a few tasks here and there, but it comes in at 24kB gzipped. I'd argue that lodash doesn't belong in most frontend apps. However, for heavy, business-oriented frontends and backend JS, it's a different story.

So, before adding a dependency to your projects, ask yourself if you truly need it and check how much [a package weighs](https://bundlephobia.com/). If you would like to go through cleaning up process, I wrote an article on [optimizing Next.js bundle size](https://devprogrammer.com/programming/how-to-reduce-nextjs-bundle-size) on [my blog](https://devprogrammer.com/).


## In Conclusion

In reflecting on the nuances of JavaScript, it's clear that certain aspects can sometimes... well, grind one's gears. While JavaScript has its quirks, being mindful of these pitfalls can lead to cleaner, more efficient code. As developers, it's our responsibility to think while we code and educate ourselves. Not only to copy and paste old code or press tab as Copilot suggestions pop up on the screen.