Categories
JavaScript Best Practices

JavaScript Best Practices — Complex and Duplicate Code and Functional Programming

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.

Remove Duplicate Code

Duplicate code is bad.

If we have to change them, we’ve to change them in multiple places.

It’s easy to miss them.

Therefore, we should remove them.

For instance, instead of writing:

function showEmployeeList(employees) {
  employees.forEach(employee => {
    const salaries = employees.salaries();
    const tasks = employees.tasks();
    const data = {
      salaries,
      tasks
    };

    display(data);
  });
}

function showManagerList(managers) {
  managers.forEach(manager => {
    const salaries = manager.salaries();
    const tasks = manager.tasks();
    const data = {
      salaries,
      tasks
    };

    display(data);
  });
}

We write:

function showWorkersList(workers) {
  workers.forEach(worker => {
    const salaries = worker.salaries();
    const tasks = worker.tasks();

    const data = {
      salaries,
      tasks
    };

    switch (worker.type) {
      case "manager":
        render(data);
      case "employee":
        render(data);
    }
  });
}

Set Default Objects with Object.assign

Setting a non-empty object as the first argument of Object.assign will return an object with all the properties of the object in the first argument merged with the ones with the other arguments.

For instance, we can write:

const obj = Object.assign({
    title: "Foo"
  },
  config
);

obj is an object with the title with the properties in config after it.

Don’t Use Flags as Function Parameters

We shouldn’t use flags as function parameters.

For instance, we can write:

function getData(name, isManager) {
  if (isManager) {
    return findManagerData(name);
  } else {
    return findWorkerData(name);
  }
}

We write:

function getManagerData(name) {
  return findtManagerData(name);
}

function getWorkerData(name) {
  return findtWorkerData(name);
}

No Side Effects

Side effects are something that’s done to something outside the function

Some side effects are unavoidable, like manipulating files and changing other external resources.

Most of the rest can be avoided.

For instance, we shouldn’t write:

let name = "foo bar";

function splitName() {
  name = name.split(" ");
}

since we can return the result in a function instead.

We can instead write:

function splitName(name) {
  return name.split(" ");
}

Don’t Manipulate Objects in Place if it’s Passed from a Function Parameter

All non-primitive values, including objects and arrays, are passed into a function by reference.

Therefore, if we modify these parameters, then we’ll change the original object.

For instance, if we have:

const addTask = (tasks, task) => {
  tasks.push(task);
};

Then the tasks array and the task object that’s passed in will be modified.

Therefore, we should return a new array instead:

const addTask = (tasks, task) => {
  return [...tasks, tasks];
};

This is a more hidden side effect that we may not have thought of.

Don’t Write to Global Functions

Never modify built-in prototypes.

Instead, we can extend them.

Extending built-in prototypes think unexpected results.

They make testing easier and prevent bugs from mutating data.

For instance, instead of writing:

Array.prototype.itemsInOtherArray = function(arr) {
  return this.filter(element => !arr.includes(element));
};

We write:

class DiffArray extends Array {
  itemsInOtherArray(arr) {
    return this.filter(element => !arr.includes(element));
  }
}

Use Functional Programming

We should use functional programming principles like not committing side effects.

Also, we make objects immutable.

Higher-order functions are also something we can use.

Some array methods are perfect examples of something that exhibit all these characteristics.

Instead of computing sums with a loop:

let totalPrice = 0;

for (const item of items) {
  totalPrice += item.price;
}

We write:

const totalPrice = items.reduce(
  (totalPrice, output) => totalPrice + item.price,
  0
);

JavaScript arrays have the reduce method to add the total price from the array entries.

Encapsulate Conditionals

If we have long boolean expressions in conditional, we can put them in a function.

For instance, instead of writing:

if (item.price > 100 && isLarge(item) && isSmart(item)) {
  // ...
}

We can write:

const isBigAndSmart = (item) => {
  return item.price > 100 && isLarge(item) && isSmart(item)
}

if (isBigAndSmart(item)) {
  // ...
}

No Negative Conditionals

We shouldn’t have negative conditional in our code.

They are hard to read.

For example, instead of writing:

if (!isPresent(node)) {
  // ...
}

We write:

if (isPresent(node)) {
  // ...
}

Conclusion

We should take advantage of functional programming features in JavaScript.

Also, we should remove duplicate code and move complex code into their own function.

Categories
JavaScript Best Practices

JavaScript Best Practices — JSX Spacing and Indentation, Fragments, Lists

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.

One Function Type for Function Components

We can stick with one function type for function components.

We can either write a regular or an arrow function.

For instance, we can write:

const Foo = function (props) {  
  return <div>{props.content}</div>;  
};

or:

const Foo = (props) => {  
  return <div>{props.content}</div>;  
};

Arrow functions are slightly shorter.

So we may want to just use that for all components.

Boolean Attributes Notation in JSX

If we have a boolean prop and we want to set it to true , we don’t have to write out the value.

For instance:

const Foo = <Bar personal />;

is the same as:

const Foo = <Bar personal={true} />;

We can just stick with the short way.

Spaces Inside of Curly Braces in JSX Attributes and Expressions

We can specify spaces in JSX attributes and expressions explicitly.

For instance, we can write:

<div>  
  hello  
  {' '}  
  <a>world</a>  
</div>

We put the space string in curly braces to make it explicit.

Closing Bracket Location in JSX

We can put closing brackets in a logical location with our JSX code.

For instance, we can write:

<Greeting firstName="jane" lastName="jones" />;

or:

<Greeting  
  firstName="jane"  
  lastName="jones"  
/>;

They’re both aligned logically.

Closing Tag Location in JSX

We should put closing tags in a location that makes it neat.

For instance, we can write:

<Greeting>  
  james  
</Greeting>

or:

<Greeting>james</Greeting>

to make it neat.

No Unnecessary Curly Braces in JSX Props and/or Children

We don’t need curly braces for string props on children.

For instance, the braces are redundant in the code below:

<Greeting>{'hello'}</Greeting>;  
<Greeting prop={'hello'} attr={"foo"} />;

We can clean them up by writing:

<Greeting>hello</Greeting>;  
<Greeting prop='hello' attr='foo' />;

Linebreaks in Curly Braces in JSX Attributes and Expressions

We should write curly braces in a neat manner.

For example, we can write:

<div>  
  { bar }  
</div>

or:

<div>  
  {  
    bar  
  }  
</div>

They’re both symmetrical.

Spaces Inside of Curly Braces in JSX Attributes and Expressions

We should have an even amount of spaces at each end of the expression.

For instance, instead of writing:

<Greeting name={ firstname} />;  
<Greeting name={firstname } />;

We write:

<Greeting name={firstname} />;

Spaces Around Equal Signs in JSX Attributes

We don’t need spaces around the equal signs in JSX attributes.

For instance, we can write:

<Greeting name={firstname} />;

Position of the First Prop

We can put the first prop inline or below the tag name.

For instance, we can write:

<Hello  
   foo  
/>

or:

<Hello foo />

Shorthand or Standard form for React fragments

Fragments are components that don’t render an element and can be used as a wrapper component.

It has a long and short form.

The long-form is <React.Fragment>...</React.Fragment> and the short form is <>...</> .

For instance, we can write:

<React.Fragment><Foo /></React.Fragment>

or:

<><Foo /></>

The long-form can have props and the short form can’t.

Event Handler Naming Conventions in JSX

Event handler prop names should start with on and the function for handling the event should start with handle .

For instance, we can write:

<Foo onChange={this.handleChange} />

or:

<Foo onChange={this.props.onBar} />

The prop names start with on and the handler method starts with handle .

JSX Indentation

We need some indentation in our JSX code.

For instance, we can write:

<App>  
  <Foo />  
</App>

to have 2 spaces for indentation.

It’s easy to read and type.

Props Indentation in JSX

We can use 2 spaces for indenting props as well.

For instance, we can write:

<Greeting  
  firstName="james"  
/>

Add Key Prop to List Items

For React to keep track of list items properly, we need a unique value for each item as the key prop’s value.

This way, React can identify the items properly no matter where it’s located.

For instance, instead of writing:

data.map(x => <Hello>{x.name}</Hello>);

We write:

data.map(x => <Hello key={x.id}>{x.name}</Hello>);

Conclusion

We should remember to add the key prop for list items.

Good spacing and indentation make reading code easier.

We can use strings with spaces to make spaces explicit.

Categories
JavaScript Best Practices

JavaScript Best Practices — Better DOM, String, and Array Methods

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.

Use .flatMap(…) over .map(…).flat()

We use flatMap over map and flat together since we can do both things with flatMap ,

for instance,e instead of writing:

[1, 2, 3].map(i => [i]).flat();

We write:

[1, 2, 3].flatMap(i => [i]);

Use .includes() Instead of .indexOf() When Checking for Existence or Non-Existence

includes is better than indexOf when checking for existence or nonexistence.

includes is shorter.

So instead of writing:

[].indexOf('foo') !== -1;

We write:

[].includes('foo');

Use Modern DOM APIs

We should use newer DOM methods than older ones.

For instance, instead of writing:

foo.replaceChild(baz, bar);

foo.insertBefore(baz, bar);

foo.insertAdjacentText('position', bar);

foo.insertAdjacentElement('position', bar);

We write:

foo.replaceWith(bar);

foo.before(bar);

foo.prepend(bar);

foo.append(bar);

Use Negative Index over .length - index for slice and splice

Negative indexes are shorter than length — index for slice and splice .

For instance, instead of writing:

foo.slice(foo.length - 3, foo.length - 1);
foo.splice(foo.length - 1, 1);

We write:

foo.slice(-3, -1);
foo.splice(-1, 1);

It’s much shorter and easier on our brains.

Use Node#append() Over Node#appendChild()

We should use append over appendChild for appending elements to a parent,.

append lets us append strings in addition to nodes.

append has a node return value.

And we can append several nodes and strings with it.

For instance, instead of writing:

foo.appendChild(bar);

We write:

foo.append(bar);
foo.append(bar, 'baz');

Use childNode.remove() over parentNode.removeChild(childNode)

We can use remove to remove a node directly instead of getting the parent and call removeChild to remove a node.

For instance, instead of writing:

parentNode.removeChild(foo);

We write:

foo.remove();

Use Number Static Properties Over Global Ones

The number static properties are better than the global ones.

Number.isNaN and Number.isFinite don’t convert the type of the argument before doing the check.

For instance, instead of writing:

const foo = parseInt('20', 2);
const foo = parseFloat('30.5');
const foo = isNaN(20);
const foo = isFinite(20);
if (Object.is(foo, NaN)) {}

We write:”

const foo = Number.parseInt('20', 2);
const foo = Number.parseFloat('30.5');
const foo = Number.isNaN(20);
const foo = Number.isFinite(20);
if (Object.is(foo, Number.NaN)) {}

Use catch Binding Parameter

If catch ‘s binding parameter isn’t used, then it should be omitted.

So instead of writing:

try {} catch (error) {}

We write:

try {} catch {}

Use .querySelector() Over .getElementById(), .querySelectorAll() Over .getElementsByClassName() and .getElementsByTagName()

querySelector is more versatile than getElementById since the former can take any selector.

querySelectorAll is more versatile than getElementsByClassName and getElementsByTagName since it takes any selector.

So instead of writing:

document.getElementById('baz');
document.getElementsByClassName('baz bar');
document.getElementsByTagName('main');

We write:

document.querySelector('main #baz .bar');
document.querySelectorAll('.baz .bar');

Use Reflect.apply() over Function#apply()

Reflect.apply is shorter and easier to understand than Function.apply .

And we know that it can never be overridden unlike Function.apply .

So instead of writing:

function foo() {}

foo.apply(null, [42]);

We write:

function foo() {}

Reflect.apply(foo, null, [42]);

Use String#replaceAll() Over Regex Searches with the Global Flag

replaceAll is easier to understand than regex search and replace to replace substrings.

For instance, instead of writing:

string.replace(/foo/g, '');

We write:

string.replaceAll('foo', '');

Use Set#has() Over Array#includes() when Checking for Existence or Non-existence

We can use has instead of includes to check for existence or nonexistence since it’s faster.

For instance, instead of writing:

const array = [1, 2, 3, 4, 5];
const hasValue = value => array.includes(value);

We write:

const set = new Set([1, 2, 3, 4, 5]);
const hasValue = value => set.has(value);

Use the Spread Operator Over Array.from()

We should use the spread operator over Array.from .

It’s a shorter way to turn array-like objects to arrays.

For instance, instead of writing:

Array.from(set).filter(() => {});

We write:

[...set].filter(() => {});

Use String#startsWith() & String#endsWith() to Check for Start and End of Strings

We should use startsWith or endsWith to check that a string starts or ends with a given string.

For instance, instead of writing:

/^bar/.test(foo);
/bar$/.test(foo);

We write:

foo.startsWith('bar');
foo.endsWith('bar');

Conclusion

We should use modern JavaScript methods to make our lives easier.

There are many strings and array methods to make our lives much easier.

We should also take note of new DOM method and use those instead of what’s available before.

Categories
JavaScript Best Practices

JavaScript Best Practices — Names, Variables, and Functions

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.

Use Meaningful and Pronounceable Variable Names

Meaning and pronounceable variable names are good.

For instance, we should write:

const currentDate = new Date();

instead of:

const yyyymmd = new Date();

Use the Same Vocabulary for the Same Type of Variable

We should use the same vocabulary for the same type of variable.

For instance, we shouldn’t have 3 names of a user.

Instead of writing:

getUserInfo();
getPersonData();
getSubscriberRecord();

for functions to get user data, we should write:

getUser();

to get user data.

Use Searchable Names

We should use searchable names.

This rules out names that are too short or too generic.

Also, we shouldn’t have magic numbers.

For instance, we shouldn’t write:

setTimeout(doSomething, 86400000);

Instead, we write:

const DAY_IN_MILLISECONDS = 86_400_000;

setTimeout(doSomething, DAY_IN_MILLISECONDS);

Use Variables to Explain

We should make variable names that explain what they hold.

For instance, we should write:

const phone = "555-555-1212";
const [areaCode, exchangeCode, lineNumber] = phone.split('-');

Now we know a phone number can be split into those numbers.

No Mental Mapping

We should avoid the need to map with our mind to the meaning of the variable.

For instance, we shouldn’t write:

phoneNumbers.forEach(p => {
  call(p);
});

We’ll forget what p means quickly.

Instead, we write:

phoneNumbers.forEach(phoneNumber => {
  call(phoneNumber);
});

Now we know the parameter for the callback is the phone number without thinking.

No Unneeded Context

We can add too much information in our identifier names.

For instance, if we have:

const car = {
  carMake: "Ford",
  carModel: "Fiesta",
  carColor: "Blue"
};

We don’t need car in front of every property name.

Instead, we write:

const car = {
  make: "Ford",
  model: "Fiesta",
  color: "Blue"
};

We know that the object holds data of a car without mentioning it in every property.

Use Default Arguments

Default arguments are much better than short-circuiting or conditionals for setting default argument values.

For instance, instead of writing:

function createPerson(name) {
  const personName = name || "james";
  // ...
}

We write:

function createPerson(name = 'james') {
  // ...
}

It’s shorter to use default arguments.

Also, || returns 'james' for all falsy arguments like empty string, null , undefined , 0 or NaN .

2 or Fewer Function Arguments Ideally

The more function arguments we have, the harder it is to use it.

We lose track of the data types and their order easily if there are more.

If we need more, we can use object parameter with destructuring, then we don’t have to worry about the order.

And we can have as many arguments as we want.

For instance, instead of writing:

function createCar(make, body, color, lengthInMeters) {
  // ...
}

createMenu("ford", "fiesta", "red", 5);

We write:

function createCar({ make, body, color, lengthInMeters }) {
  // ...
}

createCar({
  make: "ford",
  body: "fiesta",
  color: "red",
  lengthInMeters: 5
});

We pass in an object and destructure the properties to variables in the parameter.

Functions Should do One Thing

Functions that do more than one thing isn’t good.

It’s confusing and probably long.

Therefore, they should do one thing.

For instance, we should write:

function emailActiveCustomers(customers) {
  clients.filter(customerRecord).forEach(email);
}

function isActiveCustomer(customer) {
  const customerRecord = database.lookup(customer);
  return customerRecord.active;
}

instead of:

function emailClients(customers) {
  clients.forEach(customer => {
    const customerRecord = database.lookup(customer);
    if (customerRecord.active()) {
      email(client);
    }
  });
}

The emailClients function both checks if a customer is active and emails the customer.

We should split them to isAcriveCustiomer to check if a customer is active and emailActiveCustomer to email the customer.

Names Functions by What they do

We should name functions in a way that tell what they do.

For instance, instead of writing:

function add(date, years) {
  // ...
}

const date = new Date();

add(date, 1);

We write:

function addYearsToDate(date, years) {
  // ...
}

const date = new Date();

addYearsToDate(date, 1);

addYearsToDate is much clearer than add .

Conclusion

We should name variables and functions clearly.

Also, we should make names meaningful.

Functions should do one thing and not have too many arguments.

If we need more arguments, then we can accept an object and destructure it so that we don’t have to worry about the order they’re passed in.

Categories
JavaScript Best Practices

JavaScript Best Practices — No Useless Syntax

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 await Without Promises

We should use await only with promises.

It’s definitely an error if we use await with anything else.

For instance, instead of writing:

async function foo(){
  return 10;
}

We write:

async function foo(){
  const val = await aPromise;
}

No Comma Operator

We should never use the comma operator.

All it does is return the last thing in the list.

So instead of writing”

switch (foo) {
  case 1, 2, 3:
    return true;
  case 4, 5:
    return false;
}

We write:

switch (foo) {
  case 3:
    return true;
  case 5:
    return false;
}

Use Curly Braces for if, for, do, or while Statements

We should use curly braces for these statements so that we know where block starts or ends.

Instead of writing:

if (foo === 'baz')
  foo = 10;

We write:

if (foo === 'baz') {
  foo = 10;
}

This way, if we have anything below it, we won’t mistaken it to be within the block.

for-in Should have Filtered with an if Statement

If we write a for-in loop, we should filter out the inherited properties with hasOwnProperty .

For instance, instead of writing:

for (let key in obj) {
  // do something
}

We write:

for (let key in obj) {
  if (obj.hasOwnProperty(key)) {
    // do something
  }
}

No Function Constructor

We shouldn’t use the Function constructor for creating functions.

The code is in a string, which means we can’t analyze or optimize it.

And it’s also a security risk.

So instead of writing:

let multiply = new Function('a', 'b', 'return a * b');

We write:

let multiply = (a: number, b: number) => a * b

Use of Labels

Labels are only meant to be used with do , for , while , or switch statements.

They’re used with break or continue to control loops.

For instance, we write:

A:
  while (foo) {
    if (bar) {
      continue A;
    }
  }

We labeled the loop with A .

Then we used continue on it, which is run if bar is true .

Don’t Use argument Properties

We shouldn’t use arguemnt.callee to get the function that calls the function.

It makes optimization impossible.

It’s also disallowed in strict mode.

No async Without await

We shouldn’t use async without await .

If there’s no await , which means we aren’t using any promises in the function.

Then that means we don’t need it.

So instead of writing:

async function f() {
  doSomething();
}

We write:

async function f() {
  await makeRequest();
}

where makeRequest is a function that returns a promise.

No Assignment in Conditionals

Assigning values in conditions without comparison or other boolean expressions is probably a mistake.

So we should check for those.

For instance, if we have:”

if (foo == bar ){
  //...
}

We should make sure that it’s valid.

No Duplicate super Calls

In a subclass’s constructor , we only need to call super once.

If we call it more than once, we’ll get an error.

For instance, instead of writing:

class Foo extends Bar {
  constructor() {
    super(name);
    super(name);
  }
}

We write:

class Foo extends Bar {
  constructor() {
    super(name);
  }
}

No Duplicate Switch Cases

We should never have more than one case statement with the same value in a switch block.

Only the first case will be run because of short-circuiting.

For instance instead of writing:

switch (bar) {
  case 1:
    return 'foo';
  case 1:
    return 'bar';
  case 2:
    return 'baz';
}

We write:

switch (bar) {
  case 1:
    return 'foo';
  case 2:
    return 'baz';
}

No Duplicate Variable

We should never have duplicate variables in our code.

var declarations can have duplicates that aren’t picked by the JavaScript interpreter.

So we make sure that we don’t have things like:

var a = 1;
var a = 2;

We should remove one of them, or better yet, use let or const instead of var .

Conclusion

We shouldn’t have duplicate var declarations or case blocks.

Curly braces help with delimiting blocks.

await should only be used with promises.