DEV Community

Cover image for JavaScript Habits That Grind My Gears
devprogrammer
devprogrammer

Posted on • Updated on

JavaScript Habits That Grind My Gears

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:

{contacts.length
  ? contacts.map(contact => (
      <li key={contact.id}>
        {contact.firstName} {contact.lastName}
      </li>
    ))
  : null}
Enter fullscreen mode Exit fullscreen mode

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:

{contacts?.length > 0 && contacts.map(contact => (
  <li key={contact.id}>
    {contact.firstName} {contact.lastName}
  </li>
))}
Enter fullscreen mode Exit fullscreen mode

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:

contacts.length > 0
Enter fullscreen mode Exit fullscreen mode

It might be a problem if contacts are null for example. We can prevent it by writing:

contacts && contacts.length > 0
Enter fullscreen mode Exit fullscreen mode

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:

contacts?.length > 0
Enter fullscreen mode Exit fullscreen mode

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:

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);                
}        
Enter fullscreen mode Exit fullscreen mode

This can be elegantly refactored to:

const values = Object.keys(elemObj).map((key) => parseInt(key, 10));
Enter fullscreen mode Exit fullscreen mode

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:

const newArr = []
arr.forEach((el) => newArr.push(sometransformation(el)));
Enter fullscreen mode Exit fullscreen mode

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:

if(arr.indexOf(el) != -1) { … }
Enter fullscreen mode Exit fullscreen mode

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

if(arr.includes(el)) { … }
Enter fullscreen mode Exit fullscreen mode

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:

var str = "Hi, my name is " + name + " and I'm a " + occupation + ".";
Enter fullscreen mode Exit fullscreen mode

ES6 introduced a beautiful feature for constructing strings, called template literals:

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

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. If you would like to go through cleaning up process, I wrote an article on optimizing Next.js bundle size on my blog.

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.

Top comments (2)

Collapse
 
ingosteinke profile image
Ingo Steinke

You can add optional chaining to the list of overlooked modern ES builtins and write

{contacts?.length > 0 && contacts.map(contact => (
Enter fullscreen mode Exit fullscreen mode

instead of

{contacts.length > 0 && contacts.map(contact => (
Enter fullscreen mode Exit fullscreen mode

Note: I'm aware that there may be a boo-boo if contacts are null or undefined.
Developer discretion is advised, as always with JavaScript.

No more "boo-boo", better code!

Collapse
 
devprogrammer profile image
devprogrammer

You are right, I missed that. Edited the article to include that. Thanks!