Categories
JavaScript Best Practices

JavaScript Best Practices — React Components and Promises

To make code easy to read and maintain, we should follow some best practices.

In this article, we’ll look at some best practices we should follow to make everyone’s lives easier.

Prefer await to then() for reading Promise values

async and await is shorter than the callback pattern, so we can sue that instead.

For instance, instead of writing:

myPromise
  .then((val) => {
    return foo();
  })
  .then((val) => {
    return bar();
  })
  .then((val) => {
    return val;
  })

We write:

const foo = async () => {
  const val1 = await myPromise;
  const val2 = await foo();
  const val3 = await bar();
  return va3;
}

Now we have no more callbacks.

Ensures the Proper Number of Arguments are Passed to Promise Functions

We should pass in the proper argument to promise methods.

Promise.all is called with an array of promises.

Same with Promise.race .

Promise.resolve is called with the resolved value.

Promise.reject is called with the reason for rejection.

then is called a success and optionally and failure callback.

catch is called with a callback.

finally is the same as catch .

Quotes Around Object Literal Property Names

If a property name isn’t a valid identifier, then we need quote around then.

For instance, we write:

const obj = {
  "foo bar": true
};

Otherwise, we don’t need the quotes:

const obj = {
  foo: true
};

Consistent Use of Either Backticks, Double, or Single quotes

If we pick one string delimiter to use everywhere, it would be the backtick.

We can use template strings whether we need to interpolate expressions or not.

For instance, we can write:

const backtick = `backtick`;

or:

const greet = `hello ${name}`;

We can even write multiline string with them:

const backtick = `foo
bar`;

If there’s a linebreak in the string, then it’ll be rendered as such.

Require Radix Parameter

We should puyt the radix argument in the parseInt method call to make sure we parse the number with the right base.

For instance, the following:

const num = parseInt("091");

may be interpreted as an octal number.

Instead, we should write:

const num = parseInt("091", 10);

to convert it as a decimal number.

Enforces Consistent Naming for Boolean Props

In React, we shoild make sure boolean props have a consistent naming scheme to reduce confusion.

For instance, instead of writing:

import PropTypes from 'prop-types';

class Heading extends React.Component {
  render() {
    return (
      <h1>{this.props.enabled}</h1>
    );
  }
}

Heading.propTypes = {
  enabled: PropTypes.boolean
};

We write:

import PropTypes from 'prop-types';

class Heading extends React.Component {
  render() {
    return (
      <h1>{this.props.enabled}</h1>
    );
  }
}

Heading.propTypes = {
  isEnabled: PropTypes.boolean
};

The is prefix tells us it’s a boolean prop easily.

Don’t Use button Elements without an Explicit Type Attribute

We shouldn’t use the button element without an explicit type attribute.

Otherwise, we may get unexpected results.

For instance, instead of writing:

const Foo = <button>Foo</button>

We write:

const Foo = <button type='button'>Foo</button>

Default Props should have a Corresponding Non-required PropType

We should have a default prop for non-rrquire prop types.

This way, the props are never undefined .

For instance, insteaf of writing:

function FooBar({ foo, bar }) {
  return <div>{foo}{bar}</div>;
}

FooBar.propTypes = {
  foo: React.PropTypes.string.isRequired,
  bar: React.PropTypes.string
};

FooBar.defaultProps = {
  foo: "foo"
};

We write:

function FooBar({ foo, bar }) {
  return <div>{foo}{bar}</div>;
}

FooBar.propTypes = {
  foo: React.PropTypes.string.isRequired,
  bar: React.PropTypes.string
};

FooBar.defaultProps = {
  bar: "bar"
};

Enforce Consistent Usage of Destructuring Assignment of Props, State, and Context

We should use the destructuring assignment of props, state, and context consistent.

For instance, we should write:

const IdComponent = ({id}) => {
  return (<div id={id} />)
};

or:

const Foo = class extends React.PureComponent {
  render() {
    const { name } = this.state;
    return <div>{name}</div>;
  }
};

Add displayName in a React component Definition

We should set the displayNamne property so that we see the name when we debug with React dev tools.

For instance, we write:

export default function Greet({ name }) {
  return <div>hello {name}</div>;
}
Greet.displayName = 'Greet';

Conclusion

We sure make sure we define and use promises properly.

Also, when we create React components, we should have some prop validations and default props for optional ones.

Also, displayName is useful for debugging.

Categories
JavaScript Best Practices

JavaScript Best Practices — Paths, Numbers, and Operands

To make code easy to read and maintain, we should follow some best practices.

In this article, we’ll look at some best practices we should follow to make everyone’s lives easier.

No Negating the Left Operand in in Expressions

Forgetting to negate the whole in expression is a mistake.

Negating the left operand will negate nothing else.

For instance, don’t write:

if (!key in object) {
  //...
}

We write:

if (!(key in object)) {
  //...
}

to negate the whole expression.

No Nested Ternary Expressions

Nested ternary expressions are hard to read, so we shouldn’t use them/

For instance, we shouldn’t write:

const foo = bar ? baz : qux === quxx ? abc : baz;

We should just write if statements to replace it:

if (foo) {
  thing = bar;
} else if (baz === qux) {
  thing = abc;
} else {
  thing = baz;
}

No Using new For Side Effects

We shouldn’t use new for side effects.

So we shouldn’t write:

new Animal();

Instead, we write:

const animal = new Animal();

We assign the returned value to a variable so we can use it.

No Function Constructor

The Function constructor takes string arguments for creating functions.

It’s hard to optimize the code because of that, and we don’t want to let anyone pass strings to it.

Instead of writing:

const add = new Function("a", "b", "return a + b");

We write:

const x = (a, b) => {
  return a + b;
};

No Object Constructors

Object constructors shouldn’t be used.

They don’t bring any benefit and it’s longer to write.

For instance, instead of writing:

const obj = new Object();

We write:

const obj = {};

No new require

We shouldn’t use new and require together.

Instead, we separate them to remove confusion between:

const foo = new require('foo');

and

const foo= new require('foo');

So instead of writing those, we write:

const Foo = require('foo');
const foo = new Foo();

No Symbol Constructor

The Symbol function isn’t a constructor.

We call it directly to create a symbol.

So instead of writing:

const foo = new Symbol("foo");

We write:

const foo = ymbol("foo");

No Primitive Wrapper Instances

We shouldn’t use primitive wrappers to create primitive values.

They have the object types instead of the usual primitive types.

There’s no benefit to using them.

If primitive values need to be wrapped into objects, then it’ll be done temporarily automatically.

So instead of writing:

const stringObject = new String("hello");

We write:

const str = "hello";

No Calling Global Object Properties as Functions

We shouldn’t call global object properties as functions since they aren’t functions.

For instance, we shouldn’t have code like:

const math = Math();

const newMath = new Math();

const json = JSON();

const newJSON = new JSON();

const reflect = Reflect();

Instead, use their properties:

const obj = JSON.parse("{}");

const value = Reflect.get({ x: 1, y: 2 }, "x");

const pi =  Math.PI;

No Octal Literals

We shouldn’t have numbers that start with a zero.

They’re treated as octal literals.

For instance, instead of writing:

const num = 071;

is actually 57 in decimal.

So we should write:

const num = 71;

instead.

No Octal Escape Sequences in String Literals

Octal escape sequences in string literals are deprecated in ES5, so they shouldn’t be used.

So we shouldn’t write:

const foo = "hello \251";

Instead, we write:

const foo = "hello \xA9";

No Reassignment of Function Parameters

We shouldn’t reassign function parameters to a new value.

They mutate the argumenrts objects and also the variable itself if they’re objects.

So instead of writing;

function foo(bar) {
  bar = 100;
}

We write:

function foo(bar) {
  let baz = 13;
}

No String Concatenation when Using __dirname and __filename

We shouldn’t concatenate those expressions with the rest of the path since they don’t work together properly with all platforms.

So instead of writing:

const fullPath = __dirname + "/foo.js";

const filePath = __filename + "/foo.js";

We write:

const fullPath = path.join(__dirname, "foo.js");

const fullPath = path.join(__filename, "foo.js");

Conclusion

We shouldn’t concatenate paths together.

Instead, we use the path module methods to do so.

Nested ternary expressions are hard to read.

Deprecated expressions should be removed.

And never use octal literals

Categories
JavaScript Best Practices

JavaScript Best Practices — Conditionals, Classes, and Over-Optimization

To make code easy to read and maintain, we should follow some best practices.

In this article, we’ll look at some best practices we should follow to make everyone’s lives easier.

Avoid Conditionals

We can avoid conditionals and replace it with polymorphism.

For instance, instead of writing:

class Person {
  // ...
  getData() {
    switch (this.type) {
      case "student":
        return this.getStudentData();
      case "employee":
        return this.getEmployeeData();
      case "manager":
        return this.getManagerData();
    }
  }
}

We can split them into multiple subclasses:

class Person {
  // ...
}

class Student extends Person {
  // ...
  getData() {
    return this.getStudentData();
  }
}

class Employee extends Person {
  // ...
  getData() {
    return this.getEmployeeData();
  }
}

class Manager extends Person  {
  // ...
  getData() {
    return this.getManagerData();
  }
}

Instead of a big class that does multiple things, we split them into multiple subclasses.

The shared code is in Person .

Thes rest is in the methods exclusive to the subclasses.

this.getStudentData, this.getEmployeeData, and this.getManagerData can stay in the subclasses.

Don’t Over-Optimize

We shouldn’t over-optimize our code if we have to sacrifice readability.

For instance, we don’t have to cache loop lengths.

It won’t bring much difference in performance in modern browsers, but we make our code harder to read.

Instead of writing:

for (let i = 0, len = items.length; i < len; i++) {
  // ...
}

We write:

for (let i = 0; i < items.length; i++) {
  // ...
}

or just use the for-of loop:

for (const item of items) {
  // ...
}

Remove Dead Code

We should remove dead code since they aren’t used.

For instance, instead of writing

const oldRequest = (params) => {
  //...
}

const newRequest = (params) => {
  //...
}

newRequest({
  //...
});

We write:

const newRequest = (params) => {
  //...
}

newRequest({
  //...
});

Use Getters and Setters

We should use getters and stress on objects.

They let us do validation before getter and setting data.

And we can encapsulate any logic that’s run before getting.

Logging and error handling is easier, and we can lazy load our stuff in getters.

For instance, instead of writing:

function makeCart() {
  // ...

  return {
    cart: []
    // ...
  };
}

We write:

function makeCart() {
  let cart = [];

  function getCart() {
    return cart;
  }

  function setCart(newCart) {
    cart = newCart;
  }

  return {
    // ...
    getCart,
    setCart
  };
}

We create a cart with a private cart and a getCart getter function and setCart setter function.

We expose what we want in our code.

Now we can use it by writing:

const cart = makeCart();
cart.setBalance(cartItems);

Make Objects have Private Members

We can use closures to keep private members in objects.

For instance, instead of writing:

const person = {
  name: 'james',
  age: 20
}

We write:

const person = (() => {
  const age = 20;
  return {
    name: 'james'
  }
})();

We return an object with the name but not the age .

That’s hidden in the function.

This one of the ways to keep private variables.

Module members are also private unless we export them.

Prefer Class Syntax Over Constructor Functions

The constructor function makes inheritance harder.

To do inheritance, we’ve to inherit the methods and add instance methods to the prototype of the constructor.

For instance, we write:

const Animal = function(name) {
  this.name = name;
};

Animal.prototype.move = function() {};

const Dog = function(name) {
  Animal.call(this, name);
  this.age = age;
};

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {};

We’ve to create the Animal constructor and add instance methods to its prototype.

Then we create the Dog constructor and call the Animal constructor with the arguments.

Then we inherited the methods from the Animal ‘s prototype with Object.create .

And then we set the constructor property of its Prototype to itself to get the right result when we use instanceof .

Then we add instance methods exclusive to Dog .

With the class syntax, it’s much easier.

For instance, we write:

class Animal {
  constructor(name) {
    this.name = name;
  }

  move() {
    /* ... */
  }
}

class Dog extends Animal {
  constructor(age) {
    super(name);
    this.age = age;
  }

  bark() {
    /* ... */
  }
}

We put the constructor and methods all inside the class.

We call the parent constructor with super ,

And we extends to indicate inheritance.

Conclusion

The class syntax makes inheritance much easier.

Conditionals can be replaced with polymorphism.

If optimization doesn’t bring much benefit and make our code harder to read, then don’t do it.

Categories
JavaScript Best Practices

JavaScript Best Practices — Removing Duplicates

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices we should follow when writing JavaScript code.

No Duplicate Keys in Object Literals

We shouldn’t have duplicate keys in object literals.

This will yield unexpected results.

For example, we shouldn’t write:

const user = {
  name: 'james',
  name: 'joe'
}

Instead, we should remove them or name them differently.

No Duplicate case Labels in switch Statements

We should’t have duplicate case labels in our switch statements.

For example, we shouldn’t write:

switch (id) {
  case 1:
    // ...
  case 1:
    //...
}

Only the first one that matches the id value will run.

Therefore, we should combine them or remove them.

Use Single import Per Module

There should only be one import statemen per module in our code.

For example, we shouldn’t have:

import { f1 } from 'module'
import { f2 } from 'module'

Instead, we write:

import { f1, f2 } from 'module'

We save space and remove duplicates.

No Empty Character Classes in Regexes

Our regexes shouldn’t have empty character classes in them.

It’s probably a mistake to have them.

For instance, we shouldn’t write:

const myRegex = /^abc[]/;

No Empty Destructuring Patterns

If we have empty destructuring patterns, it’s probably a mistake.

Also, we probably meant to assign a default value to an empty object to a variable.

For example, we shouldn’t write:

const { a: {} } = foo

Instead, we write:

const { a = {} } = foo

which will assign an empty object to a if foo.a is undefined .

Or we can write;

const { a: { b } } = foo;

to assign something nested in a to another variable.

Don’t Use eval

eval lets us run code from a string.

This is a security risk since we can run any code inside a string.

Also, it prevents the JavaScript interpreter from optimizing our code.

Therefore, we should never use the eval function.

No Reassigning Exceptions in catch clauses

We should never reassign exceptions in catch clauses.

This will discard the original exception data.

For example, we shouldn’t write;

try {
  // ...
} catch (e) {
  e = 'value'
}

Instead, we write:

try {
  // ...
} catch (e) {
  const newVal = 'value'
}

Assigning our value to a new variable is much better than assigning something to the exception object.

Don’t Extend Native Objects

Native objects aren’t meant to be extended.

Therefore, we should just what’s built-in.

Having extra properties in the native objects confuses people and they may be surprised if they don’t exist anywhere else.

For example, we should have code like:

Object.prototype.age = 2

No Unnecessary Function Binding

We only need to call bind on a function if we need to change the value of this inside the function.

Therefore, we shouldn’t call it if we don’t need to do that.

So we shouldn’t write:

const name = function () {
  getName()
}.bind(user)

Instead, we write:

const name = function () {
  this.getName()
}.bind(user)

No Unnecessary Boolean Casts

If we have a boolean already, then we shouldn’t have to cast them.

For example, if we have:

const result = true;

Then we shouldn’t write:

if (!!result) {
  // ...
}

We can just use it as-is:

if (result) {
  // ...
}

No Unnecessary Parentheses Around Function Expressions

We don’t need to wrap a function around parentheses.

For instance, we shouldn’t have code like:

const foo = (function () { })

We don’t need the parentheses around the function expression.

Instead, we write:

const foo = function () { }

Use break to Prevent Fallthrough in switch Cases

We should always add break at the end of each case block or statement so that the code below it won’t get executed.

For instance, instead of writing:

switch (filter) {
  case 1:
    foo()
  case 2:
    bar()
}

We write:

switch (filter) {
  case 1:
    foo();
    break;
  case 2:
    bar();
    break;
}

Conclusion

If we have bad code like missing break statements, we should add them.

If we have redundant operations like converting a boolean to a boolean, then we should remove them.

Duplicates members and variables should also be eliminated.

Categories
JavaScript Best Practices

JavaScript Best Practices — Objects and Invalid Syntax

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices we should follow when writing JavaScript code.

Objects Must Contain a Getter When a Setter is Defined

Objects should have a getter when a setter is defined.

A setter without a getter isn’t very useful, so we should add one.

For instance, we write:

const person = {
  set name (value) {
    this._name = value
  },
  get name () {
    return this._name
  }
}

instead of:

const person = {
  set name (value) {
    this._name = value
  }
}

Constructors of Derived Classes Must Call super

We should call super in derived classes.

For instance, we should write:

class Cat extends Animal {
  constructor () {
    super()
    this.legs = 4;
  }
}

We’ll get an error if we don’t call super before setting properties in our class as we did in the example.

Use Array Literals Instead of Array Constructors

The Array constructor just adds extra complexity to our array declarations.

It does 2 different things.

If it has one argument passed into it, then it returns an empty array with the number of empty slots in the array.

Otherwise, it returns an array with the arguments we passed in.

Therefore, we should just use array literals.

For example, we write:

const nums = [1, 2, 3];

instead of:

const nums = new Array(1, 2, 3);

Don’t Use arguments.callee and arguments.caller

We shouldn’t use any properties in the arguments object, including the callee and caller properties.

They make code optimizations impossible. Also, they’re deprecated and are forbidden in strict mode.

Therefore, we should never use them.

Don’t Modify Variables of Class Declarations

If we have a class, then we shouldn’t assign it to something else.

This will avoid lots of confusion.

For instance, we shouldn’t have code like:

class Dog {}
Dog = 'woof';

That’s just confusing.

Avoid Modifying Variables Declared Using const

Reassigning a const variable with a new value will give us an error.

Therefore, we shouldn’t do that.

For instance, we shouldn’t have:

const score = 100;
score = 125;

Instead, we can remove the second line or create a new variable an assign the value to it.

No Constant Expressions in Conditional Statements

Constant expressions aren’t very useful in conditional statements.

This is because they always evaluate to the same thing, so it’s useless to have the conditional statement.

For example, we shouldn’t have code like:

if (false) {
  // ...
}

The code inside the block never runs since we have false as the condition.

However, we can have them in loops:

while (true) {
  // ...
}

since we may want infinite loops.

No Control Characters in Regular Expressions

We shouldn’t have control characters in regexes.

They are invisible and they’re rarely used in JavaScript strings.

Therefore, it’s probably a mistake that we include them.

So we shouldn’t have code like:

const re = /x1f/;

No debugger Statements

The debugger statement is used for adding breakpoints in our code so that we can inspect the variables.

However, we should remember to remove them when we’re done with them so that our app won’t pause when it’s in production.

No delete Operator on Variables

The delete operator is used for removing properties.

Therefore, they shouldn’t be used on variables.

So we shouldn’t write:

let foo;
delete foo;

No Duplicate Arguments in Function Signatures

We should never have duplicate arguments in function signatures.

It’s invalid syntax and will give us unexpected results.

For example, we shouldn’t write:

function sum (a, a, b) {
  // ...
}

Instead, we write:

function sum (a, b, c) {
  // ...
}

No Duplicate Name in Class Members

We shouldn’t have 2 class members with the same name.

That will yield unexpected results.

For example, we shouldn’t have:

class Cat {
  meow() {}
  meow() {}
}

We should name them with different names.

Conclusion

We shouldn’t use operators in invalid ways in our JavaScript code.

Also, we shouldn’t have duplicate expressions where we shouldn’t have them.

Any extraneous characters and debugger statements should be removed from our code.